Secure Storage of Cryptographic Hashes in Active Directory: Best Practices for OctetString Attributes with Restricted Read Permissions


2 views

When working with Active Directory, developers often face the dilemma of securely storing cryptographic hashes or private keys. The standard approach of using built-in OctetString attributes comes with significant security concerns, particularly regarding default read permissions.

Most OctetString attributes in Active Directory grant read access to all authenticated users by default. This creates a security vulnerability where:

  • Hashes become accessible domain-wide
  • Attackers could build rainbow tables
  • Sensitive data appears in LDAP queries

After extensive testing in Windows Server 2008R2/2012R2 environments with Exchange 2010/2013, these attributes show promise for secure storage:

// PowerShell example to check attribute permissions
Get-ADObject -Identity "CN=Schema,CN=Configuration,DC=domain,DC=com" |
Get-ADObject -Properties defaultSecurityDescriptor |
Select-Object -ExpandProperty defaultSecurityDescriptor

For maximum security, consider extending the AD schema with custom attributes:

# Schema extension example
$ldif = @"
dn: CN=mY-PrivateHash,CN=Schema,CN=Configuration,DC=domain,DC=com
changetype: add
objectClass: attributeSchema
cn: mY-PrivateHash
attributeId: 1.3.6.1.4.1.99999.1.1.1
attributeSyntax: 2.5.5.10
oMSyntax: 4
isSingleValued: TRUE
searchFlags: 1
systemFlags: 16
adminDisplayName: mY-PrivateHash
adminDescription: Custom attribute for secure hash storage
"@

$ldif | Out-File -FilePath ".\schemaextension.ldf"
ldifde -i -f .\schemaextension.ldf -j .\ -k

After selecting or creating an attribute, apply strict permissions:

# DSACLS example for permission lockdown
dsacls "CN=User1,OU=Employees,DC=domain,DC=com" /G "SYSTEM:F" "DOMAIN\KeyMgmtService:F" "DOMAIN\Domain Admins:RP"
dsacls "CN=User1,OU=Employees,DC=domain,DC=com" /D "Authenticated Users"
  • Always encrypt data before storage, even in "secure" attributes
  • Implement proper ACL inheritance blocking
  • Consider using AD LDS for extremely sensitive data
  • Regularly audit access using tools like BloodHound

When AD storage isn't feasible, consider:

  • Azure Key Vault integration
  • Enterprise PKI systems
  • Dedicated HSM appliances
  • Windows DPAPI with delegated access

When working with Active Directory, we often need to store sensitive data like cryptographic hashes or private keys. The fundamental requirement is finding an attribute that:

  • Uses OctetString syntax (binary data capable)
  • Has restrictive default permissions
  • Isn't replicated unnecessarily
  • Isn't indexed by default

From your screenshot and requirements, we need to avoid attributes like:

userCertificate
userSMIMECertificate
thumbnailPhoto

These are problematic because they're either publicly readable or frequently used by other applications.

For Windows Server 2016 and later, consider:

// PowerShell example for storing data
$hash = [System.Convert]::ToBase64String($yourBinaryData)
Set-ADUser -Identity "user1" -Replace @{'msDS-KeyCredentialLink'=$hash}

This attribute was specifically designed for storing key material and has proper security controls.

When built-in options don't suffice:

# Schema extension example
$ldif = @"
dn: CN=PrivateHash,CN=Schema,CN=Configuration,DC=domain,DC=com
changetype: add
attributeID: 1.2.840.113556.1.8000.9999
attributeSyntax: 2.5.5.10
isSingleValued: TRUE
oMSyntax: 4
objectClass: attributeSchema
adminDisplayName: PrivateHash
adminDescription: For storing sensitive hashes
searchFlags: 1  # NOT_REPLICATED
"@

$ldif | Out-File schema.ldf
ldifde -i -f schema.ldf -j .

For any attribute you choose, lock down permissions:

# PowerShell ACL modification
$attribute = Get-ADObject "CN=PrivateHash,CN=Schema,CN=Configuration,DC=domain,DC=com"
$acl = Get-Acl "AD:\$($attribute.DistinguishedName)"
$acl.SetAccessRuleProtection($true, $false)
$acl.Access | Where-Object {$_.IdentityReference -ne "NT AUTHORITY\SYSTEM"} | ForEach-Object {
    $acl.RemoveAccessRule($_)
}
Set-Acl "AD:\$($attribute.DistinguishedName)" $acl

Always validate your configuration:

# Test reading the attribute
try {
    Get-ADUser user1 -Properties PrivateHash -ErrorAction Stop
    Write-Warning "Attribute is readable - permissions not set correctly"
} catch {
    Write-Host "Access denied - proper security confirmed"
}