Awesome
OTS-Share (One-Time Secret Share)
A self-hosting app to share secrets only one-time.
Content
- Features
- How to run
- Request and response
- How to use
- CLI usage
- Change configurations
- Tech stack
- Using with deploy tools
- Format of the generated URL
- Road map
- Todo
Features
- Support both texts and small files (maximum
1MB
). - Creates shareable links which valid for a maximum of 24 hours.
- The contents are encrypted with
AES
inCBC
mode, with a256-bit
key. (Using Crypto-js) - Passwords are NOT sent to the backend server.
- The app periodically deletes encrypted content after it expires, and the encrypted content gets deleted once the web UI fetches it.
- CLI support.
- Multiple database connectivity support.
Mongo
Postgres
MySQL
How to run
With the default database
This application is entirely run in Docker and comes with Mongo 4.2
image. (view the docker-compose.yml
for further reference.)
To execute this app, simply run following command.
make start
Without the default database
This application can connect to a external database.
(Currently support Postgres
and Mysql
).
To execute this app, simply run following command.
# Set the connection string to your database.
export DB_URL=mysql://root:root@host.docker.internal:3306/ots_share
make start-no-db
OR
Change the modify the DB_URL
variable under ots-share-run-no-db
service in docker-compose.yml
,
and then run
make start-no-db
Access UI
After that, the application is accessible via http://localhost:8282
Request and Response
Request
Create record request body
A sample request body is as follows.
{
"content": "U2FsdGVkX1+XUedzb2748LeKmf9UpN9hVWjBDUwJfXs=",
"expireIn": {
"value": 10,
"unit": "minutes"
}
}
Property | type | is required | purpose |
---|---|---|---|
content | string | yes | Encrypted content |
expireIn | object | yes | Expiration configurations |
expireIn .value | number | yes | numerical value of expiration. E,g 1, 2 |
expireIn .unit | enum ('days' , 'hours' ) | yes | Unit of expiration. |
-
Sample
Create
request.
curl 'http://localhost:8282/api/record' -H 'Content-Type: application/json' \
--data-raw \
'{
"content" : "U2FsdGVkX1+bozD8VjexiUeHJ3BfdxrXCmRyai8V0hY=",
"expireIn": {
"value": 1,
"unit": "minutes"
}
}'
--compressed
-
Sample
GET
request.
curl 'http://localhost:8282/api/record/b2nC422huavXfMs2DWZ2Z9' -H 'Content-Type: application/json'
Response
A sample record body is as follows.
{
"id": "iN2jS3y1pstio7JVXs1zLF",
"slug": "iN2jS3y1pstio7JVXs1zLF",
"content": "U2FsdGVkX1+XUedzb2748LeKmf9UpN9hVWjBDUwJfXs=",
"expiary": "2023-02-12T14:55:41.510Z",
"status": "avaiable",
"created_at": "2023-02-12T14:45:41.521Z"
}
Property | type | is required | purpose |
---|---|---|---|
id | string | yes | Primary key of the record |
slug | string | yes | For future use (Primary key of the record) |
content | string | yes | Encrypted content |
expiary | string (Date ) | yes | Expiration date and time |
status | enum ('avaiable' , 'unavaiable' ) | yes | For future use. |
created_at | string (Date ) | yes | Record created date |
How to use
(Please don't lose the generated URL. There is no way to retrieve the content or regenerate the URL !!!)
For text
- Visit the landing page and select
Text
from top menu. - Add your secret content to the
Secret content
text box.
- Click the
Create
Button. - Copy the
URL
in the text box. (Click theCopy Icon
).
- (Please don't lose the generated URL. There is no way to retrieve the content or regenerate the URL !!!)
- Send the copied URL to the other party via a secure channel.
For files
- Visit the landing page and select
File
from top menu. - Click on the
"Drag 'n' drop"
area or drag and drop a file.
-
(Pleas refer to screen regarding the file size limits).
-
Upload a file.
-
Click the
Create
Button. -
Copy the
URL
in the text box. (Click theCopy Icon
).
- (Please don't lose the generated URL. There is no way to retrieve the content or regenerate the URL !!!)
- Send the copied URL to the other party via a secure channel.
View secret content in the shared link.
- Visit the shared link using a browser.
- You will see the following screen.
- Click
Fetch Content
.
For text
-
You'll see the following screen.
-
Click the
"Click there to view the content"
. -
You will see the content as follows.
For files
-
You'll see the following screen.
-
Click the
"Click here to download the file"
button to download the file.
Errors.
In case of an error, the following screen will appear.
CLI usage
-
Support only for
texts
. -
You can use the
CLI
to utilizeAPIs
. -
Encryption using CLI
#!/bin/bash
# Configs
PASSWORD="pass-key"
OTS_SHARE_DOMIN="http://host.docker.internal:8282"
OTS_SHARE_API="$OTS_SHARE_DOMIN/api/record"
OPENSSL_PARAMETERS_PASSWORD="-pass pass:$PASSWORD"
OPENSSL_PARAMETERS_ALGORITHM="-base64 -aes-256-cbc -pbkdf2"
text_to_encrypt="test string to encrypt"
################
## Encryption ##
################
# Record expiration value. A numerical value
RECORD_EXPIRATION_VALUE=10
# Record expiration unit. It can be "minutes" or "hours"
RECORD_EXPIRATION_UNIT="minutes"
# 1. Generate encrypted string
encrypted_content=$(echo $text_to_encrypt | openssl enc -e $OPENSSL_PARAMETERS_ALGORITHM $OPENSSL_PARAMETERS_PASSWORD)
# 2. Make API call OTS-Share and retrieve the Id
# We need this id for encryption
record_id=$(\
curl -s "$OTS_SHARE_API" \
-H 'Content-Type: application/json' \
--data-raw \
'{ "content" : "'$encrypted_content'", "expireIn": { "value": '$RECORD_EXPIRATION_VALUE', "unit": "'$RECORD_EXPIRATION_UNIT'" }}' \
--compressed \
| jq '.id' \
| tr -d '"' \
)
#### Encryption results
echo "!!! Keep these safe !!!"
echo "-----------------------------------"
echo "Record id: $record_id"
echo "Password: $PASSWORD"
echo "-----------------------------------"
echo "(This record will expires in: $RECORD_EXPIRATION_VALUE $RECORD_EXPIRATION_UNIT)"
</details>
<details>
<summary>Output encryption</summary>
!!! Keep these safe !!!
-----------------------------------
Record id: b2nC422huavXfMs2DWZ2Z9
Password: pass-key
-----------------------------------
(This record will expires in: 10 minutes)
</details>
- Decryption using CLI
#!/bin/bash
# Configs
PASSWORD="pass-key"
OTS_SHARE_DOMIN="http://host.docker.internal:8282"
OTS_SHARE_API="$OTS_SHARE_DOMIN/api/record"
OPENSSL_PARAMETERS_PASSWORD="-pass pass:$PASSWORD"
OPENSSL_PARAMETERS_ALGORITHM="-base64 -aes-256-cbc -pbkdf2"
$record_id="b2nC422huavXfMs2DWZ2Z9" # ID from previous encryption operation
################
## DECRYPTION ##
################
# 1. Fetch content
content=$(\
curl "$OTS_SHARE_API/$record_id" \
-s -H 'Content-Type: application/json' \
--compressed \
| jq '.content' \
| tr -d '"' \
)
# 2. Decrypt
decrypted_content=$(echo $content | openssl enc -d $OPENSSL_PARAMETERS_ALGORITHM $OPENSSL_PARAMETERS_PASSWORD)
echo "-----------------------------------"
echo "Content: $decrypted_content"
echo "-----------------------------------"
</details>
<details>
<summary>Output decryption</summary>
-----------------------------------
Content: test string to encrypt
-----------------------------------
</details>
Change configurations
All the configurations are mentioned in the docker-compose.yml
under or ots-share-run-no-db
service.
- Change default port to access the application are available in
docker-compose.yml
underots-share-run
orots-share-run-no-db
service. - You can modify the
mongo-local
service indocker-compose.yml
to keep the data persistent.
Change database server.
-
Please change the
DEV_PORT
variable thedocker-compose.yml
underots-share-run
orots-share-run-no-db
service. -
Please change the
DEV_PORT
variable thedocker-compose.yml
underots-share-run-no-db
service to connect to external database. -
DB_URL
must be a connection string.- The app parse the
DB_URL
as anURL
and use theprotocol
to identify thedatabase
driver. - sample connection strings:
mongodb://mongo-local/ots-share
- forMongo
postgres://db:db@host.docker.internal:5432/ots_share
- forPostgres
mysql://root:root@host.docker.internal:3306/ots_share
- forMySQL
- The app parse the
Change the default server port.
- Please change
SERVER_PORT
variable in the indocker-compose.yml
underots-share-run
orots-share-run-no-db
service.
Change the purge process interval.
- Default value is 1 minute.
- Please set
PURGE_TRIGGER_INTERVAL
variable in the indocker-compose.yml
underots-share-run
orots-share-run-no-db
service. - The
PURGE_TRIGGER_INTERVAL
value must be inmilliseconds
.
Tech stack
-
UI:
- React
- Material UI
-
Server:
- Typescript
- Node
- Express
-
DB support:
MongoDB
- (default DB
)Postgres
MySQL
Using with deploy tools
With https://www.portainer.io/
-
Step 1 & 2.
-
Step 3.
-
Press Deploy the stack to deploy.
Format of the generated URL
The URL format, which required sending to the other party, is as follows. The id
received from the backend API gets concatenated with the password. After that, the contaminated string gets encoded into Base 58
.
The format is as follows.
<hosted-domain>/r/Base58Encoded(id-from-api : password : type : file-name)
- Possible values for
type
is'text'
or'file'
. type
andfile-name
is optional.type
is default totext
if not mentioned.file-name
is available for file.- It supports
Base 64
encoding.
Road map
- A Chrome extension
- A Slack app
Todo
- Add
Contribution
instructions. - Learn more ReactJs. :smile:
- Fix any bugs. :smile: