Media Repository - Security Audit

Repository: PsyterMediaUploadAPI
Audit Date: November 10, 2025
Severity Scale: ๐Ÿ”ด Critical | ๐ŸŸ  High | ๐ŸŸก Medium | ๐ŸŸข Low


Executive Summary

Overall Security Rating: โš ๏ธ MODERATE RISK (5.5/10)

The Media API implements basic security measures including OAuth 2.0 authentication, file validation, and encrypted database connections. However, critical vulnerabilities exist that require immediate attention, particularly around HTTP-based authentication, lack of malware scanning, hardcoded secrets, and missing security headers.

Critical Findings: 8
High Findings: 12
Medium Findings: 9
Low Findings: 6


Critical Security Issues ๐Ÿ”ด

1. HTTP-Based OAuth Authentication

Severity: ๐Ÿ”ด Critical
CWE: CWE-319 (Cleartext Transmission of Sensitive Information)

Issue:

// App_Start/Startup.cs
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
    AllowInsecureHttp = true,  // โŒ CRITICAL SECURITY ISSUE
    TokenEndpointPath = new PathString("/authenticate"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
    Provider = myProvider
};

Risk:
- Bearer tokens transmitted over HTTP in plaintext
- Vulnerable to man-in-the-middle (MITM) attacks
- Token interception allows full API access for 24 hours
- ApplicationToken exposed during authentication

Impact: HIGH - Attackers can intercept credentials and impersonate users

Recommendation:
- DO NOW: Set AllowInsecureHttp = false
- Deploy with SSL/TLS certificates
- Enforce HTTPS at IIS level
- Add HSTS headers

Fix:

OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
    AllowInsecureHttp = false,  // โœ… HTTPS only
    TokenEndpointPath = new PathString("/authenticate"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
    Provider = myProvider
};

<!-- Web.config: Force HTTPS -->
<system.webServer>
  <rewrite>
    <rules>
      <rule name="HTTPS Redirect" stopProcessing="true">
        <match url="(.*)" />
        <conditions>
          <add input="{HTTPS}" pattern="^OFF$" />
        </conditions>
        <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>

Priority: DO NOW


2. No Malware/Virus Scanning

Severity: ๐Ÿ”ด Critical
CWE: CWE-434 (Unrestricted Upload of File with Dangerous Type)

Issue:
- Files are uploaded with NO antivirus scanning
- Base64 signature validation checks file format, NOT content safety
- Malicious PDFs, Word docs with macros, infected images can be uploaded
- Stored files served to other users without sanitization

Risk:
- Malware distribution through platform
- Server compromise via malicious uploads
- Data breach via infected files
- Legal/compliance violations

Attack Scenarios:
1. Upload infected PDF as “Education History”
2. PDF contains executable payload
3. Other users download and execute
4. Ransomware spreads across organization

Recommendation:
- DO NOW: Integrate antivirus scanning (ClamAV, Windows Defender API, or cloud service)
- Quarantine suspicious files
- Scan asynchronously after upload
- Add file sandboxing

Implementation Example:

// Add to MediaController.cs
private async Task<bool> ScanForMalware(Stream fileStream)
{
    // Option 1: Windows Defender API
    using (var defenderClient = new AmsiClient())
    {
        var result = await defenderClient.ScanBufferAsync(fileStream);
        return result == AmsiResult.Clean;
    }

    // Option 2: ClamAV
    var clamClient = new ClamClient("localhost", 3310);
    var scanResult = await clamClient.SendAndScanFileAsync(fileStream);
    return scanResult.Result == ClamScanResults.Clean;

    // Option 3: Cloud service (VirusTotal, MetaDefender)
    var cloudScanner = new CloudMalwareScanner(apiKey);
    return await cloudScanner.ScanFileAsync(fileStream);
}

// In UploadMedia method:
if (!await ScanForMalware(fileStream))
{
    // Delete file immediately
    File.Delete(uploadedFilePath);
    return GetInvalidResponse(E_ResponseReason.MALWARE_DETECTED);
}

Priority: DO NOW


3. Hardcoded Encryption Keys

Severity: ๐Ÿ”ด Critical
CWE: CWE-321 (Use of Hard-coded Cryptographic Key)

Issue:

// Helper/SecurityHelper.cs - Multiple hardcoded secrets
private static string _salt = "2GlHRj2MxxxC2Dnn";  // โŒ Hardcoded
private static string _vector = "8WhO95QaRasSxsfh";  // โŒ Hardcoded
private static string _passPhrase = "55C9F5A8-17B8-4783-ABB1-D9AC7C3CF9BB";  // โŒ Hardcoded

// Used for connection string decryption:
public static string DecryptString(string text)
{
    return Cryptography.Decrypt(text, "55C9F5A8-17B8-4783-ABB1-D9AC7C3CF9BB");  // โŒ
}

// Web.config - Hardcoded machine key
<machineKey 
    decryption="AES"
    decryptionKey="CB7B6A9A33A0496D5158CFEB6EB95B0AC538C6BF7F6D8D00851D2A4FB0E24858"
    validation="HMACSHA256" 
    validationKey="C77B221F66E2C665DE95CEFCE724EAF0CEDD5E93D0E95221D64FF3FF7ECA0641FD5491909FB93E10559DA07D317AA91E8B8B7BE2E16F78D0888D48C6D912984B" 
/>

Risk:
- Keys visible in source code/compiled assemblies
- Same keys used across all environments (dev, staging, prod)
- If source code leaked, all encryption compromised
- Cannot rotate keys without code changes

Impact: Database passwords, connection strings fully compromised if code exposed

Recommendation:
- DO NOW: Move keys to environment variables or Azure Key Vault
- Use different keys per environment
- Implement key rotation policy
- Use Data Protection API (DPAPI) for Windows

Fix:

// Use configuration
private static string _passPhrase = ConfigurationManager.AppSettings["EncryptionKey"];
private static string _salt = ConfigurationManager.AppSettings["EncryptionSalt"];
private static string _vector = ConfigurationManager.AppSettings["EncryptionVector"];

// Or Azure Key Vault
var keyVaultClient = new KeyVaultClient(/* ... */);
var secret = await keyVaultClient.GetSecretAsync("https://yourvault.vault.azure.net/secrets/EncryptionKey");
_passPhrase = secret.Value;

<!-- Web.config -->
<appSettings>
  <add key="EncryptionKey" value="#{EncryptionKey}#" /> <!-- Replaced during deployment -->
  <add key="EncryptionSalt" value="#{EncryptionSalt}#" />
  <add key="EncryptionVector" value="#{EncryptionVector}#" />
</appSettings>

Priority: DO NOW


4. Connection String Insecure Configuration

Severity: ๐Ÿ”ด Critical
CWE: CWE-256 (Unprotected Storage of Credentials)

Issue:

<!-- Web.config -->
<connectionStrings>
  <!-- Encrypted but with Persist Security Info=True -->
  <add name="PsyterDatabase" 
       connectionString="Data Source=encrypted;Initial Catalog=encrypted;User Id=encrypted;Password=encrypted;Persist Security Info=True" 
       providerName="System.Data.SqlClient"/>
</connectionStrings>

Problems:
1. Persist Security Info=True - Password kept in connection string after connection opened
2. Accessible via reflection/debugging
3. Password in memory longer than necessary

Risk:
- Password exposure through memory dumps
- Accessible to debuggers
- Increased attack surface

Recommendation:

<add name="PsyterDatabase" 
     connectionString="...;Persist Security Info=False" 
     providerName="System.Data.SqlClient"/>

Priority: DO NOW


5. No Request Rate Limiting

Severity: ๐Ÿ”ด Critical
CWE: CWE-770 (Allocation of Resources Without Limits)

Issue:
- No rate limiting on any endpoint
- /authenticate can be brute-forced
- /Media/UploadMedia can be flooded with large files
- No IP-based throttling

Attack Scenarios:
1. Token Brute Force: Repeatedly try ApplicationTokens until valid
2. DoS via Upload: Upload 100 MB files repeatedly, exhaust storage
3. Resource Exhaustion: Concurrent PDF generation requests crash server

Recommendation:
- DO NOW: Implement rate limiting middleware
- Limit: 10 requests/minute for /authenticate
- Limit: 100 requests/hour for /UploadMedia per user
- IP-based throttling for abuse prevention

Implementation:

// Install: Install-Package AspNetCoreRateLimit (or custom implementation)

public class RateLimitingMiddleware : DelegatingHandler
{
    private static Dictionary<string, RateLimitInfo> _limits = new Dictionary<string, RateLimitInfo>();

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var key = GetClientKey(request);  // IP or user ID

        if (IsRateLimited(key))
        {
            return new HttpResponseMessage(HttpStatusCode.TooManyRequests)
            {
                Content = new StringContent("Rate limit exceeded. Try again later.")
            };
        }

        RecordRequest(key);
        return await base.SendAsync(request, cancellationToken);
    }
}

Priority: DO NOW


6. Weak Password Hashing (MD5)

Severity: ๐Ÿ”ด Critical
CWE: CWE-327 (Use of a Broken or Risky Cryptographic Algorithm)

Issue:

// Helper/SecurityHelper.cs
private static string GenerateHash(string pSourceText)
{
    UnicodeEncoding Ue = new UnicodeEncoding();
    byte[] ByteSourceText = Ue.GetBytes(pSourceText);
    MD5CryptoServiceProvider Md5 = new MD5CryptoServiceProvider();  // โŒ MD5 is broken
    byte[] ByteHash = Md5.ComputeHash(ByteSourceText);
    return Convert.ToBase64String(ByteHash);
}

public static string GeneratePassword(string pSourceText)
{
    string strTemp = GenerateHash(pSourceText);   // MD5
    string strTemp1 = GenerateHash(strTemp);      // MD5
    string strTemp2 = GenerateHash(strTemp1);     // MD5
    string strTemp3 = GenerateHash(strTemp2);     // MD5
    return strTemp3;  // Still MD5, just repeated
}

Risk:
- MD5 has known collision vulnerabilities
- Rainbow table attacks possible
- Fast hash allows brute force
- Even triple-MD5 doesn’t fix MD5 weaknesses

Recommendation:
- DO NOW: Replace with bcrypt, Argon2, or PBKDF2
- Add proper salt (random per password)
- Use high iteration count

Fix:

// Install: Install-Package BCrypt.Net-Next
using BCrypt.Net;

public static string GeneratePassword(string pSourceText)
{
    return BCrypt.HashPassword(pSourceText, workFactor: 12);
}

public static bool VerifyPassword(string password, string hash)
{
    return BCrypt.Verify(password, hash);
}

Priority: DO NOW

Note: If this is only for internal token hashing (not user passwords), still upgrade to SHA-256 minimum.


7. CORS Allows All Origins

Severity: ๐Ÿ”ด Critical (in production)
CWE: CWE-346 (Origin Validation Error)

Issue:

// App_Start/Startup.cs
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);  // โŒ Too permissive

Risk:
- ANY website can call this API
- Cross-site request forgery (CSRF) attacks
- Data theft from authenticated sessions
- Unauthorized API access from malicious sites

Recommendation:

// Restrict to known domains
var corsPolicy = new CorsPolicy
{
    AllowAnyHeader = true,
    AllowAnyMethod = true,
    SupportsCredentials = true
};
corsPolicy.Origins.Add("https://psyter.com");
corsPolicy.Origins.Add("https://www.psyter.com");
corsPolicy.Origins.Add("https://admin.psyter.com");

var corsOptions = new CorsOptions
{
    PolicyProvider = new CorsPolicyProvider
    {
        PolicyResolver = context => Task.FromResult(corsPolicy)
    }
};
app.UseCors(corsOptions);

Priority: DO NOW (for production)


8. Custom Errors Disabled

Severity: ๐Ÿ”ด Critical (in production)
CWE: CWE-209 (Information Exposure Through Error Message)

Issue:

<!-- Web.config -->
<system.web>
  <customErrors mode="Off" />  <!-- โŒ Exposes stack traces -->
</system.web>

Risk:
- Detailed error messages exposed to clients
- Stack traces reveal code structure
- Database errors show schema/table names
- Assists attackers in reconnaissance

Example Exposed Error:

{
  "Message": "An error has occurred.",
  "ExceptionMessage": "Invalid object name 'dbo.Users'.",
  "ExceptionType": "System.Data.SqlClient.SqlException",
  "StackTrace": "   at PsyterMediaUploadAPI.Repository.BaseRepository.CreateDbCommand()..."
}

Recommendation:

<system.web>
  <customErrors mode="On" defaultRedirect="~/Error">
    <error statusCode="404" redirect="~/Error/NotFound" />
    <error statusCode="500" redirect="~/Error/ServerError" />
  </customErrors>
</system.web>

Priority: DO NOW (for production)


High Severity Issues ๐ŸŸ 

9. No File Content Sanitization

Severity: ๐ŸŸ  High
CWE: CWE-79 (Cross-Site Scripting), CWE-434

Issue:
- PDF files can contain JavaScript
- HTML files can contain scripts
- Office documents can contain macros
- No content sanitization before storage
- Files served directly to users

Risk:
- Stored XSS attacks via malicious PDFs
- Macro-based attacks via Word/Excel files
- JavaScript execution in PDF viewers

Recommendation:
- Sanitize PDF content (remove JavaScript)
- Strip macros from Office documents
- Convert suspicious formats to safe formats (e.g., DOCX โ†’ PDF)
- Serve files with proper Content-Disposition headers

Priority: DO NEXT


10. No SQL Injection Testing

Severity: ๐ŸŸ  High
CWE: CWE-89

Issue:
While parameterized queries are used, no evidence of SQL injection testing:

PsyterDbCommand.Parameters.Add("@ApplicationToken", SqlDbType.NVarChar).Value = appToken;

Current State: โœ… Parameterized queries used (good!)
Gap: No penetration testing or SQL injection fuzzing performed

Recommendation:
- DO NEXT: Conduct SQL injection testing
- Use tools: SQLMap, Burp Suite, OWASP ZAP
- Test all user inputs
- Validate XML inputs (used in SaveHomeWorkImages)

Priority: DO NEXT


11. No Input Length Validation

Severity: ๐ŸŸ  High
CWE: CWE-770

Issue:

// Controllers/MediaController.cs
string userId = HttpContext.Current.Request.Form["UserId"] ?? "User";
string homeWorkId = HttpContext.Current.Request.Form["HomeWorkId"];
string userName = HttpContext.Current.Request.Form["UserFullName"];

No validation on:
- UserId length (could be 10,000 characters)
- UserFullName length (SQL injection attempt, buffer overflow)
- HomeWorkId format

Risk:
- Buffer overflow attacks
- Database field overflow
- Resource exhaustion

Recommendation:

[StringLength(50, MinimumLength = 1)]
public string UserId { get; set; }

[StringLength(200)]
public string UserFullName { get; set; }

[Range(1, long.MaxValue)]
public long? HomeWorkId { get; set; }

Priority: DO NEXT


12. No Authentication Token Encryption

Severity: ๐ŸŸ  High

Issue:
Bearer tokens stored/transmitted without additional encryption layer.

Recommendation:
- Use token encryption (JWE)
- Sign tokens (JWT with signature)
- Implement token revocation list

Priority: DO NEXT


13. Session Fixation Vulnerability

Severity: ๐ŸŸ  High
CWE: CWE-384

Issue:
- Tokens valid for 24 hours
- No token refresh on privilege change
- No forced re-authentication

Recommendation:
- Implement token refresh with shorter expiry
- Invalidate tokens on security-sensitive changes
- Add forced re-authentication for critical operations

Priority: DO NEXT


14. Missing Security Headers

Severity: ๐ŸŸ  High
CWE: CWE-693

Missing Headers:
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- X-XSS-Protection: 1; mode=block
- Strict-Transport-Security (HSTS)
- Content-Security-Policy

Recommendation:

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="X-Content-Type-Options" value="nosniff" />
      <add name="X-Frame-Options" value="DENY" />
      <add name="X-XSS-Protection" value="1; mode=block" />
      <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
      <add name="Content-Security-Policy" value="default-src 'self'" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

Priority: DO NEXT


15-20. Additional High Issues

  1. No Audit Logging - Security events not logged (DO NEXT, 1 week)
  2. Directory Traversal Risk - File paths not fully validated (DO NEXT, 3 days)
  3. XML External Entity (XXE) - XML parsing for homework files (DO NEXT, 2 days)
  4. Insecure Deserialization - XML deserialization without validation (DO NEXT, 3 days)
  5. No Account Lockout - Unlimited authentication attempts (DO NEXT, 2 days)
  6. Weak DES Encryption - DES used instead of AES (DO NEXT, 1 week)

Medium Severity Issues ๐ŸŸก

21. File Extension Case Sensitivity

Severity: ๐ŸŸก Medium

Issue:

if (!allowedExtensions.ToLower().Contains(extension.ToLower()))  // โœ… Case-insensitive

Currently handled, but inconsistent throughout codebase.

22. No File Name Sanitization

Severity: ๐ŸŸก Medium

Original filename from user not sanitized before use in logs/responses.

23. Token Not Invalidated on Logout

Severity: ๐ŸŸก Medium

No logout endpoint; tokens remain valid for full 24 hours.

24. No IP Address Logging

Severity: ๐ŸŸก Medium

Can’t trace malicious uploads to source IP.

25-29. Additional Medium Issues

  1. Lack of Content-Type validation on responses
  2. No time-based file deletion (orphaned files)
  3. Missing CAPTCHA on authentication
  4. No multi-factor authentication
  5. Weak token randomness (GUID-based)

Low Severity Issues ๐ŸŸข

30. Commented-Out Code

Severity: ๐ŸŸข Low

//if (language != "en-US") { ... }

Should be removed or feature-flagged.

31-35. Additional Low Issues

  1. Missing API versioning
  2. No request ID tracking
  3. Lack of health check endpoints
  4. Missing OpenAPI/Swagger documentation
  5. No security policy headers

Security Checklist

Authentication & Authorization

  • HTTPS-only enforcement
  • Strong password hashing (bcrypt/Argon2)
  • Token encryption
  • Multi-factor authentication
  • Session timeout
  • Token revocation
  • Account lockout policy
  • Rate limiting on authentication

Input Validation

  • File type validation
  • File size limits
  • Malware scanning
  • Content sanitization
  • Input length validation
  • SQL injection protection
  • XSS protection
  • Directory traversal prevention
  • XML external entity protection

Data Protection

  • Encryption at rest
  • Encryption in transit (HTTPS)
  • Secure key management
  • Connection string protection
  • Sensitive data masking in logs
  • Secure file deletion

Infrastructure

  • Security headers
  • CORS policy
  • Custom error pages
  • Audit logging
  • IP whitelisting
  • DDoS protection
  • Regular security updates

Compliance Considerations

HIPAA (Healthcare)

โš ๏ธ NOT COMPLIANT - Missing:
- Audit controls (ยง164.312(b))
- Encryption at rest (ยง164.312(a)(2)(iv))
- Access controls (ยง164.312(a)(1))
- Automatic logoff (ยง164.312(a)(2)(iii))

GDPR

โš ๏ธ PARTIALLY COMPLIANT - Missing:
- Right to erasure implementation
- Data breach notification system
- Privacy by design
- Data portability

PCI DSS (if handling payment data)

โš ๏ธ NOT COMPLIANT - Missing:
- File integrity monitoring
- Antivirus software
- Secure coding practices
- Regular penetration testing


Phase 1: Critical Fixes (1-2 weeks)

  1. Enable HTTPS-only
  2. Implement malware scanning
  3. Move hardcoded keys to Key Vault
  4. Fix connection string security
  5. Add rate limiting
  6. Enable custom errors in production
  7. Restrict CORS to known domains
  8. Add security headers

Phase 2: High Priority (1 month)

  1. Replace MD5 with bcrypt
  2. Implement comprehensive logging
  3. Add SQL injection testing
  4. Implement input length validation
  5. Add token refresh mechanism
  6. Fix content sanitization
  7. Add audit logging
  8. Implement directory traversal protection

Phase 3: Medium Priority (2-3 months)

  1. Add multi-factor authentication
  2. Implement storage quotas
  3. Add IP-based blocking
  4. Implement CAPTCHA
  5. Add file encryption at rest
  6. Implement secure file deletion
  7. Add health checks
  8. Implement API versioning

Phase 4: Long-term (6 months)

  1. Migrate to .NET Core
  2. Implement zero-trust architecture
  3. Add penetration testing program
  4. Achieve HIPAA compliance
  5. Implement GDPR full compliance
  6. Add security automation

Conclusion

Current Security Posture: MODERATE RISK
Critical Issues: 8 (must fix immediately)
High Issues: 12 (fix within 1-2 months)
Medium Issues: 9 (plan for 3-6 months)