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¶
- No Audit Logging - Security events not logged (DO NEXT, 1 week)
- Directory Traversal Risk - File paths not fully validated (DO NEXT, 3 days)
- XML External Entity (XXE) - XML parsing for homework files (DO NEXT, 2 days)
- Insecure Deserialization - XML deserialization without validation (DO NEXT, 3 days)
- No Account Lockout - Unlimited authentication attempts (DO NEXT, 2 days)
- 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¶
- Lack of Content-Type validation on responses
- No time-based file deletion (orphaned files)
- Missing CAPTCHA on authentication
- No multi-factor authentication
- 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¶
- Missing API versioning
- No request ID tracking
- Lack of health check endpoints
- Missing OpenAPI/Swagger documentation
- 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
Recommended Security Roadmap¶
Phase 1: Critical Fixes (1-2 weeks)¶
- Enable HTTPS-only
- Implement malware scanning
- Move hardcoded keys to Key Vault
- Fix connection string security
- Add rate limiting
- Enable custom errors in production
- Restrict CORS to known domains
- Add security headers
Phase 2: High Priority (1 month)¶
- Replace MD5 with bcrypt
- Implement comprehensive logging
- Add SQL injection testing
- Implement input length validation
- Add token refresh mechanism
- Fix content sanitization
- Add audit logging
- Implement directory traversal protection
Phase 3: Medium Priority (2-3 months)¶
- Add multi-factor authentication
- Implement storage quotas
- Add IP-based blocking
- Implement CAPTCHA
- Add file encryption at rest
- Implement secure file deletion
- Add health checks
- Implement API versioning
Phase 4: Long-term (6 months)¶
- Migrate to .NET Core
- Implement zero-trust architecture
- Add penetration testing program
- Achieve HIPAA compliance
- Implement GDPR full compliance
- 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)