Skip to content
This repository has been archived by the owner on Mar 16, 2023. It is now read-only.

Commit

Permalink
fixed read-only mode, introduced share-only mode, introduced human-re…
Browse files Browse the repository at this point in the history
…adable public key download 403 response codes on errors
  • Loading branch information
yahesh committed Nov 14, 2019
1 parent 592da95 commit 1a3fb90
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 102 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<secret>" https://example.com/
# OR #
curl -X POST -d "secret=<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" <secret sharing link>
curl -X POST -d "plain" <secret-sharing-link>
# OR #
curl -X POST <secret-sharing-link>?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
Expand Down Expand Up @@ -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.
Expand Down
33 changes: 24 additions & 9 deletions actions/pub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
91 changes: 48 additions & 43 deletions actions/read.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
59 changes: 32 additions & 27 deletions actions/share.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion config/config.php.default
Original file line number Diff line number Diff line change
Expand Up @@ -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);

22 changes: 10 additions & 12 deletions index.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

# Shared-Secrets v0.22b0
# Shared-Secrets v0.23b0
#
# Copyright (c) 2016-2019, SysEleven GmbH
# All rights reserved.
Expand Down Expand Up @@ -55,6 +55,11 @@
define("READ_ONLY", false);
}

# prepare share-only mode
if (!defined("SHARE_ONLY")) {
define("SHARE_ONLY", false);
}

# prepare request method
define("REQUEST_METHOD", strtolower($_SERVER["REQUEST_METHOD"]));

Expand All @@ -72,26 +77,19 @@

# prepare URI
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
# handle URL encoded URIs
if (false !== strpos($uri, URL_ENCODE_MARKER)) {
$uri = urldecode($uri);
}
# remove leading slash
if (0 === stripos($uri, "/")) {
$uri = substr($uri, 1);
}
define("SECRET_URI", $uri);
define("SECRET_URI", nolead($uri, "/"));

# prepare action name, show read page by default
$action = READ_PAGE_NAME;
# show share page if no URI is given
if (empty(SECRET_URI)) {
# show share page if no URI is given
$action = SHARE_PAGE_NAME;
} else {
} elseif (in_array(SECRET_URI, array(HOW_PAGE_NAME, IMPRINT_PAGE_NAME, PUB_PAGE_NAME))) {
# show pages based on page URI
if (in_array(SECRET_URI, array(HOW_PAGE_NAME, IMPRINT_PAGE_NAME, PUB_PAGE_NAME))) {
$action = SECRET_URI;
}
$action = SECRET_URI;
}
define("SECRET_ACTION", $action);

Expand Down
2 changes: 1 addition & 1 deletion pages/how/get.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<h3>Get the correct public key.</h3>
<p>First of all you have to retrieve the correct public key to encrypt your secret:<br/>
<pre>wget -O "./secrets.pub" "<?= html(trail(SECRET_SHARING_URL, "/")) ?>pub"</pre></p>
<pre>wget -O "./secrets.pub" "<?= html(trail(SECRET_SHARING_URL, "/")) ?>pub?plain"</pre></p>

<h3>Encrypt the secret you want to share.</h3>
<p>To create a secret sharing link you have to do certain steps that are decribed here:
Expand Down
Loading

0 comments on commit 1a3fb90

Please sign in to comment.