ancarda / security-headers

A PHP library that helps with generating security headers
https://packagist.org/packages/ancarda/security-headers
MIT License
0 stars 0 forks source link

Allow specifying a file hash in the Content-Security-Policy header #1

Open ancarda opened 6 years ago

ancarda commented 6 years ago

A user on IRC asked for the following:

I want a method on CSP where I can give a file and have its hash added to the list

Zegnat commented 6 years ago

Are hashes supported for all directives?

Also needs to decide what to generate the hash from. Either have the user specify the hash:

public function withScriptsFromHash(string $format, string $hash): self

This would allow you to validate that the hash matches the amount of data required from the format, and that an actual CSP supported format is being used.

Alternatively you could have them specify the string that needs to be hashed:

public function withScriptsFromCode(string $code, string $format = self::DEFAULT_HASH): self

Or even from a file:

public function withScriptsFromFile(\SplFileObject $file, string $format = self::DEFAULT_HASH): self

I am not sure which one would be the most useful.

ancarda commented 6 years ago

Are hashes supported for all directives?

As far as I can tell, only scripts, stylesheets, and images, but I'm not sure.

I am not sure which one would be the most useful.

I like withScriptsFromHash and withScriptsFromFile, especially taking an \SplFileObject for the latter. Both functions would be useful as sometimes you may not have the file on hand to calculate the hash, and the SplFileObject based function can just call the string hash function once it's done whatever work is necessary to get the hash (just hash_file?)

I will possibly call them withHashOfScript and withFileHashOfScript as I think that's more understandable?

Zegnat commented 6 years ago

just hash_file?

Yeah, that works well. At that point using \SplFileObject is really just a way to assure the user is really giving you a file. I believe it should guarantee that you do not need any other checks.

public function withFileHashOfScript(\SplFileObject $file, string $format = self::DEFAULT_HASH): self
{
    return $this->withHashOfScript(hash_file($format, $file), $format);
}

I will possibly call them withHashOfScript and withFileHashOfScript as I think that's more understandable?

As I just wrote that method, I didn’t really like withFileHashOfScript as a method name. Because “file hash” can be read to mean the hash of a file, but this method specifically takes a file and not the hash.

Zegnat commented 6 years ago

Oh, I just realised you will need a check.

private function isValidHashFormat(string $format): bool
ancarda commented 6 years ago

As far as I know, the third parameter of hash_file might need true as the hash should be binary, as far as I know:

hash_file($format, $file, true);

Then it needs to be run through base64_encode.

“file hash” can be read to mean the hash of a file, but this method specifically takes a file and not the hash.

I agree, however, I'm concerned if the API is called withScript or something, it may confuse users because they aren't whitelisting the domain that file is on, just the hash. I guess this is unlikely to happen... I just wanted to ensure the word hash was present somewhere.

I'm going to hopefully start implementing this tonight, so I'll see how the functions look once they've been created.

private function isValidHashFormat(string $format): bool

How do we determine if the hash is valid? I believe hashes in CSP are always base64 encoded binary hashes, usually SHA256, but I'll see if I can find a list of supported hashing algorithms.

Zegnat commented 6 years ago

Oof. Apparently Sunday morning ideas are only half baked. You are right that you need the raw output and base64_encode it yourself!

You also need two checks.

  1. Checking the user supplied format (which was what I was thinking of previously) to make sure it is sha256, sha384, or sha512 (see spec).
  2. Checking if the hash length is the expected length for the specified format. Length of the raw hash (after base64_decode) is the only way to check if a hash is valid, AFAIK.
ancarda commented 6 years ago

Thank you for your help so far! I'm going to start working on this soon

peter279k commented 6 years ago

Is this feature implemented?

I think I can help us to implement this feature.