diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ca449..19c1d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0.23b0 (2019-11-14) + +* fixed read-only mode and introduced share-only mode +* introduced human-readable page for downloading the public key under `/pub` +* changed the download of the plain public key to `/pub?plain` +* on errors the application now returns `403 Forbidden` response codes instead of `200 OK` response codes +* updated README to reflect the new features + # 0.22b0 (2019-11-13) * improved URL parsing diff --git a/README.md b/README.md index a3f84f0..9d58d5f 100644 --- a/README.md +++ b/README.md @@ -12,22 +12,40 @@ To protect your secret from getting known by the server or an attacker, you can ### Share a Secret -Simply enter your secret on the default page of the Shared-Secrets service. You can decide to password-protect the entered secret before sending it to the server by checking the "Password-protected:" box, entering your password and pressing the "Protect!" button. After that, press the "Share the Secret!" button. The secret will be encrypted and converted into a secret sharing link. +Simply enter your secret on the default page of the Shared-Secrets service. You can decide to password-protect the entered secret before sending it to the server by checking the "Password-protected:" box, entering your password and pressing the "Protect!" button. After that, press the "Share the Secret!" button. The secret will be encrypted and converted into a secret sharing link. In cases where you need the plain secret sharing link to be returned by the web page you can append the GET parameter `?plain` to the URL of the default page. Secret sharing links can also be created by using a simple POST request: ``` curl -X POST -d "plain&secret=" https://example.com/ + +# OR # + +curl -X POST -d "secret=" https://example.com/?plain ``` ### Read a Secret -To retrieve the secret, simply open the secret sharing link and press the "Read the Secret!" button. Should your secret be password-protected, check the "Password-protected:" box, enter your password and read your actual secret by pressing the "Unprotect!" button. +To retrieve the secret, simply open the secret sharing link and press the "Read the Secret!" button. Should your secret be password-protected, check the "Password-protected:" box, enter your password and read your actual secret by pressing the "Unprotect!" button. In cases where you need the plain secret to be returned by the web page you can append the GET parameter `?plain` to the secret sharing link **but be aware** that returning the plain secret does not support the Browser-based decryption. Secrets can also be retrieved using a simple POST request: ``` -curl -X POST -d "plain" +curl -X POST -d "plain" + +# OR # + +curl -X POST ?plain +``` + +### Download the Public Key + +To download the public key of a Shared-Secrets instance in order to manually generate secret sharing links, simply visit the `/pub` page. In cases where you need the plain public key to be returned by the web page you can append the GET parameter `?plain` to the URL. + +The public key can also be downloaded using a simple GET request: + +``` +curl -X GET https://example.com/pub?plain ``` ## Installation @@ -164,6 +182,10 @@ openssl genrsa -out ./rsa.key 2048 Copy the `config/config.php.default` file to `config/config.php` and set the necessary configuration items. +### Read-Only and Share-Only Instances + +The configuration allows you to set your instances into read-only and/or share-only mode. This can be useful if want to use a private **share-only** instance or custom software to create secret sharing sharing links but provide a public **read-only** instance to retrieve the generated secret sharing links. + ### TLS Recommendation It is strongly recommended to use TLS to protect the connection between the server and the clients. diff --git a/actions/pub.php b/actions/pub.php index cee7ec5..cf35956 100644 --- a/actions/pub.php +++ b/actions/pub.php @@ -3,18 +3,33 @@ # prevent direct access if (!defined("SYS11_SECRETS")) { die(""); } - function get_public_key() { + function get_public_key(&$error) { $result = null; + $error = false; - # for shared-secrets we only support encryption with one key - $keys = array_keys(RSA_PRIVATE_KEYS); - $pubkey = open_pubkey(RSA_PRIVATE_KEYS[$keys[count($keys)-1]]); - if (null !== $pubkey) { - try { - $result = get_keypem($pubkey); - } finally { - openssl_pkey_free($pubkey); + # only proceed when the read-only mode is not enabled + if (!READ_ONLY) { + # for shared-secrets we only support encryption with one key + $keys = array_keys(RSA_PRIVATE_KEYS); + $pubkey = open_pubkey(RSA_PRIVATE_KEYS[$keys[count($keys)-1]]); + if (null !== $pubkey) { + try { + $result = get_keypem($pubkey); + } finally { + openssl_pkey_free($pubkey); + } + } else { + if (DEBUG_MODE) { + $error = "Public key could not be read."; + } } + } else { + $error = "The creation of secret sharing links is disabled."; + } + + # set default error if non is given + if ((null === $result) && (false === $error)) { + $error = "An unknown error occured."; } return $result; diff --git a/actions/read.php b/actions/read.php index d77d696..2fe9179 100644 --- a/actions/read.php +++ b/actions/read.php @@ -7,76 +7,81 @@ function read_secret($secret, &$error = null) { $result = null; $error = false; - # handle secret decoding - $secret = parse_secret_url($secret); + # only proceed when the share-only mode is not enabled + if (!SHARE_ONLY) { + # handle secret decoding + $secret = parse_secret_url($secret); - # only proceed when the secret is not empty - if (!empty($secret)) { - $keys = array_keys(RSA_PRIVATE_KEYS); - $recipients = []; - foreach ($keys as $key) { - $privkey = open_privkey(RSA_PRIVATE_KEYS[$key]); - if (null !== $privkey) { - $recipients[] = $privkey; - } - } - - try { - $decrypted_secret = decrypt_v01($secret, $recipients, $decrypt_error, $fingerprint); - } finally { - $keys = array_keys($recipients); + # only proceed when the secret is not empty + if (!empty($secret)) { + $keys = array_keys(RSA_PRIVATE_KEYS); + $recipients = []; foreach ($keys as $key) { - openssl_pkey_free($recipients[$key]); + $privkey = open_privkey(RSA_PRIVATE_KEYS[$key]); + if (null !== $privkey) { + $recipients[] = $privkey; + } } - zeroize_array($recipients); - } + try { + $decrypted_secret = decrypt_v01($secret, $recipients, $decrypt_error, $fingerprint); + } finally { + $keys = array_keys($recipients); + foreach ($keys as $key) { + openssl_pkey_free($recipients[$key]); + } - if (null !== $decrypted_secret) { - if ($link = mysqli_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, MYSQL_PORT)) { - try { - if ($statement = mysqli_prepare($link, MYSQL_WRITE)) { - $fingerprint = bin2hex($fingerprint); + zeroize_array($recipients); + } + + if (null !== $decrypted_secret) { + if ($link = mysqli_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, MYSQL_PORT)) { + try { + if ($statement = mysqli_prepare($link, MYSQL_WRITE)) { + $fingerprint = bin2hex($fingerprint); - if (mysqli_stmt_bind_param($statement, "s", $fingerprint)) { - if (mysqli_stmt_execute($statement)) { - if (1 === mysqli_affected_rows($link)) { - $result = $decrypted_secret; + if (mysqli_stmt_bind_param($statement, "s", $fingerprint)) { + if (mysqli_stmt_execute($statement)) { + if (1 === mysqli_affected_rows($link)) { + $result = $decrypted_secret; + } else { + $error = "Secret has already been retrieved."; + } } else { - $error = "Secret has already been retrieved."; + if (DEBUG_MODE) { + $error = "Insert statement could not be executed"; + } } } else { if (DEBUG_MODE) { - $error = "Insert statement could not be executed"; + $error = "Insert statement parameters could not be bound."; } } } else { if (DEBUG_MODE) { - $error = "Insert statement parameters could not be bound."; + $error = "Insert statement could not be prepared."; } } - } else { - if (DEBUG_MODE) { - $error = "Insert statement could not be prepared."; - } + } finally { + mysqli_close($link); + } + } else { + if (DEBUG_MODE) { + $error = "Database connection could not be established."; } - } finally { - mysqli_close($link); } } else { if (DEBUG_MODE) { - $error = "Database connection could not be established."; + $error = "Decryption failed: $decrypt_error"; } } } else { if (DEBUG_MODE) { - $error = "Decryption failed: $decrypt_error"; + $error = "The secret must not be empty."; } } } else { - if (DEBUG_MODE) { - $error = "The secret must not be empty."; - } + $error = "The retrieval of secret sharing links is disabled."; } # set default error if non is given diff --git a/actions/share.php b/actions/share.php index ed3b48c..0accbd3 100644 --- a/actions/share.php +++ b/actions/share.php @@ -7,43 +7,48 @@ function share_secret($secret, &$error = null) { $result = null; $error = false; - # only proceed when the secret is not empty - if (!empty($secret)) { - # only proceed when the secret is not too long - if (MAX_PARAM_SIZE >= strlen($secret)) { - # for shared-secrets we only support encryption with one key - $keys = array_keys(RSA_PRIVATE_KEYS); - $pubkey = open_pubkey(RSA_PRIVATE_KEYS[$keys[count($keys)-1]]); - if (null !== $pubkey) { - try { - $recipients = [$pubkey]; + # only proceed when the read-only mode is not enabled + if (!READ_ONLY) { + # only proceed when the secret is not empty + if (!empty($secret)) { + # only proceed when the secret is not too long + if (MAX_PARAM_SIZE >= strlen($secret)) { + # for shared-secrets we only support encryption with one key + $keys = array_keys(RSA_PRIVATE_KEYS); + $pubkey = open_pubkey(RSA_PRIVATE_KEYS[$keys[count($keys)-1]]); + if (null !== $pubkey) { try { - $encrypted_secret = encrypt_v01($secret, $recipients, $encrypt_error); - } finally { - zeroize_array($recipients); - } + $recipients = [$pubkey]; + try { + $encrypted_secret = encrypt_v01($secret, $recipients, $encrypt_error); + } finally { + zeroize_array($recipients); + } - if (null !== $encrypted_secret) { - # return the secret sharing URL - $result = get_secret_url($encrypted_secret); - } else { - if (DEBUG_MODE) { - $error = "Encryption failed: $encrypt_error"; + if (null !== $encrypted_secret) { + # return the secret sharing URL + $result = get_secret_url($encrypted_secret); + } else { + if (DEBUG_MODE) { + $error = "Encryption failed: $encrypt_error"; + } } + } finally { + openssl_pkey_free($pubkey); + } + } else { + if (DEBUG_MODE) { + $error = "Public key could not be read."; } - } finally { - openssl_pkey_free($pubkey); } } else { - if (DEBUG_MODE) { - $error = "Public key could not be read."; - } + $error = "The secret must at most be ".MAX_PARAM_SIZE." characters long."; } } else { - $error = "The secret must at most be ".MAX_PARAM_SIZE." characters long."; + $error = "The secret must not be empty."; } } else { - $error = "The secret must not be empty."; + $error = "The creation of secret sharing links is disabled."; } # set default error if non is given diff --git a/config/config.php.default b/config/config.php.default index d5c254e..aede24f 100644 --- a/config/config.php.default +++ b/config/config.php.default @@ -33,6 +33,13 @@ # this is the default timezone for the execution of the script define("DEFAULT_TIMEZONE", "Europe/Berlin"); - # this enables or disables the read-only mode of the instance + # this enables or disables the read-only mode of the instance, + # by using the read-only mode you need another instance to create secret sharing links, + # this separation can be useful if you only want to be internally able to create links define("READ_ONLY", false); + # this enables or disables the share-only mode of the instance, + # by using the share-only mode you need another instance to read secret sharing links, + # this separation can be useful if you only want to be internally able to create links + define("SHARE_ONLY", false); + diff --git a/index.php b/index.php index 246d795..9c6bb95 100644 --- a/index.php +++ b/index.php @@ -1,6 +1,6 @@ Get the correct public key.

First of all you have to retrieve the correct public key to encrypt your secret:
-

wget -O "./secrets.pub" "pub"

+
wget -O "./secrets.pub" "pub?plain"

Encrypt the secret you want to share.

To create a secret sharing link you have to do certain steps that are decribed here: diff --git a/pages/pub/get.php b/pages/pub/get.php index 9f34308..8b7393a 100644 --- a/pages/pub/get.php +++ b/pages/pub/get.php @@ -3,9 +3,46 @@ # prevent direct access if (!defined("SYS11_SECRETS")) { die(""); } - # set correct content type - header("Content-Type: application/x-pem-file"); + # define page title + define("PAGE_TITLE", "Download the Public Key."); - # output public key pem - print(get_public_key()); + $pubkey = get_public_key($error); + + # set the correct response code on error + if ((null === $pubkey) || (false !== $error)) { + http_response_code(403); + } + + if (PLAIN_PARAM) { + # set correct content type + header("Content-Type: application/x-pem-file"); + + if ((null !== $pubkey) && (false === $error)) { + print($pubkey); + } + } else { + if ((null !== $pubkey) && (false === $error)) { + $pubkey = html($pubkey); + } else { + $pubkey = "ERROR: ".html($error); + } + + # include header + require_once(ROOT_DIR."/template/header.php"); + + # prevents cache hits with wrong CSS + $cache_value = md5_file(__FILE__); + +?> + +

Download the Public Key:

+

+ + + + class="active">Share a secret. class="active">How does this service work? + class="active">Download the public key. class="active">Who provides this service?