Secret
Secret input is an input type used to pass secrets and sensitive information to action backends, the values sent via the secret input go through an additional layer of encryption using your private key. In addition, the values sent via the secret input are not logged or saved by Port.
๐ก Common Secret Usageโ
The secret input type can be used for sensitive information, such as:
- Cloud secrets
- Passwords
- API keys
- Secret tokens
Secret Input Structureโ
A secret input is defined as a regular input, but with the additional encryption
field which specifies the encryption algorithm to use:
{
"mySecretInput": {
"title": "My secret input",
"icon": "My icon",
"type": "string",
"encryption": "aes256-gcm",
"description": "My entity input"
}
}
- aes256-gcm - This will encrypt the property data using 256 bits AES in GCM mode. The encrypted value will be prefixed by the 16 bits IV and suffixed by the 16 bits MAC, encoded to base-64. The encryption key will be the first 32 bytes of your organization's Client Secret.
Supported Typesโ
- String Secret
- Object Secret
{
"mySecretInput": {
"title": "My secret input",
"icon": "My icon",
"type": "string",
"encryption": "aes256-gcm",
"description": "My entity input"
}
}
Note: it is unsupported to have the a format
for secrets inputs.
{
"mySecretInput": {
"title": "My secret input",
"icon": "My icon",
"type": "object",
"encryption": "aes256-gcm",
"description": "My entity input"
}
}
Handling the Payloadโ
The payload sent to your infrastructure will contain the encrypted value of your secret property inputs. To make use of your secret inputs, you will need to decrypt them:
Examplesโ
- AES 256 GCM
Examples for decrypting properties encrypted with the aes256-gcm
algorithm.
- Python Webhook
- NodeJs Webhook
The following example uses the flask
and pycryptodome
packages:
import base64
import json
import os
from flask import Flask, request
from Crypto.Cipher import AES
PORT_CLIENT_SECRET = 'YOUR PORT CLIENT SECRET'
PROPERY_IS_JSON = False # whether the property is defined as json or not (string otherwise)
app = Flask(__name__)
@app.route('/', methods=['POST'])
def webhook():
# initialize the aes cipher
key = PORT_CLIENT_SECRET[:32].encode()
req = request.get_json(silent=True, force=True)
encrypted_property_value = base64.b64decode(req.get('payload').get('properties').get('secret-property'))
iv = encrypted_property_value[:16]
ciphertext = encrypted_property_value[16:-16]
mac = encrypted_property_value[-16:]
cipher = AES.new(key, AES.MODE_GCM, iv)
# decrypt the property
decrypted_property_value = cipher.decrypt_and_verify(ciphertext, mac)
property_value = json.loads(decrypted_property_value) if PROPERY_IS_JSON else decrypted_property_value
return property_value # this is the original value the user sent
if __name__ == '__main__':
port = int(os.getenv('PORT', 80))
print("Starting app on port %d" % port)
app.run(debug=False, port=port, host='0.0.0.0')
The following example uses the express
package and node's built-in crypto module:
const express = require("express");
const bodyParser = require("body-parser");
const crypto = require("node:crypto");
const PORT_CLIENT_SECRET = "YOUR PORT CLIENT SECRET";
const PROPERY_IS_JSON = false; // whether the property is defined as json or not (string otherwise)
const port = 80;
const ENCODING = "utf8";
const ALGORITHM_NAME = "aes-256-gcm";
const ALGORITHM_IV_SIZE = 16;
const ALGORITHM_TAG_SIZE = 16;
const app = express();
app.post("/", bodyParser.json(), (req, res) => {
// deconstruct the property
const raw_property_value = req.body.payload.properties["secret-property"];
const property_value_buffer = Buffer.from(raw_property_value, "base64");
const iv = property_value_buffer.subarray(0, ALGORITHM_IV_SIZE);
const data = property_value_buffer.subarray(
ALGORITHM_IV_SIZE,
property_value_buffer.length - ALGORITHM_TAG_SIZE
);
const authTag = property_value_buffer.subarray(
property_value_buffer.length - ALGORITHM_TAG_SIZE
);
// initialize the aes decipher
const encodedSecret = Buffer.from(PORT_CLIENT_SECRET.substring(0, 32));
const decipher = crypto.createDecipheriv(ALGORITHM_NAME, encodedSecret, iv);
decipher.setAuthTag(authTag);
// decrypt the property
const decrypted_property_buffer = Buffer.concat([
decipher.update(data),
decipher.final(),
]);
// encode the value
const decrypted_property_value = decrypted_property_buffer.toString(ENCODING);
const property_value = PROPERY_IS_JSON
? JSON.parse(decrypted_property_value)
: decrypted_property_value;
return property_value; // this is the original value the user sent
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});