Data Security Overview
PubNub is serious about the security of your data. Using TLS and AES256 encryption algorithms, and with HIPAA, GDPR, SOC2 type 2, and CCPA compliance, you can be sure your data is safeguarded.
There are several ways in which PubNub helps to secure your app:
- Connection security secures the traffic between your client and PubNub
- Message encryption provides end-to-end security for every message, in transit and at rest
- File encryption provides end-to-end security for every file you upload, in transit and at rest
- Access management provides a detailed permission schema for your app
User ID / UUID
User ID is also referred to as UUID
/uuid
in some APIs and server responses but holds the value of the userId
parameter you set during initialization.
Connection security
PubNub offers connection level security through TLS Encryption (Transport Layer Security). It's enabled by default and generally there is no reason to disable it for any production application.
Configuring TLS
TLS is enabled by default. If you need to disable it for any reason, it can be done during the PubNub object initialization.
SSL
Some PubNub SDKs, the parameter name is ssl
(a legacy name for TLS) but it represents the latest connection encryption technology available. SSL v3 was deprecated due to a security exploit that was discovered and TLS was developed to replace it as the industry standard. Many will refer to TLS as SSL out of habit.
- JavaScript
- Swift
- Objective-C
- Java
- C#
- Python
var pubnub = new PubNub({
subscribeKey: "mySubscribeKey",
publishKey: "myPublishKey",
userId: "myUniqueUserId",
ssl: false // default 'true'
});
import PubNubSDK
let config = PubNubConfiguration(
publishKey: "demo",
subscribeKey: "demo",
userId: "myUniqueUserId",
useSecureConnections: false // default true
)
let pubnub = PubNub(configuration: config)
PNConfiguration *pnconfig = [PNConfiguration configurationWithPublishKey:@"myPublishKey"
subscribeKey:@"mySubscribeKey"];
pnconfig.uuid = "theClientUUID"
pnconfig.TLSEnabled = NO; // default YES
self.client = [PubNub clientWithConfiguration:pnconfig];
PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId("yourUserId"), "yourSubscribeKey");
// publishKey from Admin Portal (only required if publishing)
configBuilder.publishKey("PublishKey");
configBuilder.secure(false);
PubNub pubNub = PubNub.create(configBuilder.build());
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"));
pnConfiguration.PublishKey = "myPublishKey";
pnConfiguration.SubscribeKey = "mySubscribeKey";
pnConfiguration.Secure = false; // default true
Pubnub pubnub = new Pubnub(pnConfiguration);
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
pnconfig = PNConfiguration()
pnconfig.publish_key = "myPublishKey"
pnconfig.subscribe_key = "mySubscribeKey"
pnconfig.user_id = "myUserID"
pnconfig.ssl = False # default True
pubnub = PubNub(pnconfig)
TCP connection troubleshooting
If you need to capture the TCP traffic (a tcpdump to a .pcap
file) to analyze and debug the network traffic, you may need to disable TLS in your development environment. This level of analysis is only necessary to troubleshoot the most difficult network connection and communication issues that are typically outside of the PubNub domain.
Message encryption
PubNub SDKs allow you to configure the encryption/decryption algorithms using pre-bundled packages for encrypting/decrypting data (cryptors) in your apps through crypto modules.
Crypto module
A crypto module is the part of PubNub SDKs that contains multiple cryptor implementations that enable the encryption and decryption of data within the application. It can be configured to use different encryption algorithms. By default, PubNub SDKs offer crypto modules with the following encryption/decryption capabilities:
- Legacy encryption with 128-bit cipher key entropy (legacy module)
- Recommended AES-CBC 256-bit (CBC block cipher mode) encryption
If you're using one of the latest PubNub SDKs, your clients can decrypt content encrypted using either of the modules, regardless of the one you use for data encryption.
If you use the legacy module and your data is encrypted with 128-bit cipher key entropy, you can still decrypt data encrypted using the recommended AES-CBC 256-bit module. This backward compatibility works both ways - if you use the recommended 256-bit module, you can decrypt data encrypted using the legacy module.
This way, you can switch to the recommended AES-CBC 256-bit encryption and still interact with historical messages or messages sent from older clients.
Legacy encryption with 128-bit cipher key entropy
You don't have to change your encryption configuration if you want to keep using the legacy encryption. If you want to use the recommended 256-bit AES-CBC encryption, you must explicitly set that in PubNub config.
If you do not explicitly select either module in your app and have cipherKey
and, optionally, useRandomInitializationVector
set in PubNub configuration, the client defaults to using the legacy encryption.
For information on how to configure modules, refer to the Configuration documentation of each SDK.
Keep your clients up to date
Apps not using the latest SDK version will not be able to decrypt data encrypted using the 256-bit AES-CBC cipher. Make sure to update your clients or encrypt data using the legacy module.
Upgrade to 256-bit encryption
For information on how to migrate to the recommended encryption algorithm, refer to 256-bit AES-CBC Encryption.
Encryption scope
Depending on whether you want to encrypt all messages and files or just some of them, create and register a crypto module differently.
-
To fully encrypt every message and file in all channels for a given API key, specify and register the crypto module as part of the PubNub configuration when you create the object.
-
To only encrypt some or partially encrypt messages, do not specify the crypto module as part of the PubNub configuration. Instead, create a standalone instance of a crypto module and call one of the available
encrypt()
ordecrypt()
methods.
Encrypting messages
To configure encryption for all messages and files in all channels for a given API key, you need to register a module, provide a cipherKey
, and, optionally, decide whether to use a random initialization vector to encrypt (and decrypt) data.
For information on partial encryption, refer to the Partial Message Encryption section.
- JavaScript
- Swift
- Objective-C
- Java
- Go
- Kotlin
- Rust
- Ruby
- Dart
- C#/Unity
var pubnub = new PubNub({
...
// all necessary config options
cryptoModule: PubNub.CryptoModule.aesCbcCryptoModule({cipherKey: 'pubnubenigma'})
});
let pubnub = PubNub(
configuration: PubNubConfiguration(
...
// all necessary config options
cryptorModule: CryptorModule.aesCbcCryptoModule(with: "pubnubenigma")
)
)
...
// all necessary config options
config.cryptoModule = [PNCryptoModule AESCBCCryptoModuleWithCipherKey:@"enigma"
randomInitializationVector:YES];
PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId("yourUserId"), "yourSubscribeKey");
// publishKey from Admin Portal (only required if publishing)
configBuilder.publishKey("PublishKey");
configBuilder.cryptoModule(CryptoModule.createAesCbcCryptoModule("enigma", true));
...
// all necessary config options
PubNub pubNub = PubNub.create(configBuilder.build());
config := pubnub.NewConfigWithUserId(UserId("myUniqueUserId"))
// all necessary config options
config.CryptoModule = crypto.NewAesCbcCryptoModule("cipherKey", true)
pn := pubnub.NewPubNub(config)
val config = PNConfiguration(UserId("myUserId"))
...
config.cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma")
val pubnub = PubNub.create(config)
let client = PubNubClientBuilder::with_transport(Transport)
...
// all necessary config options
.with_cryptor(CryptoModule::new_aes_cbc_module("enigma", true)?)
.build()?;
pubnub = Pubnub.new(
...
# all necessary config options
crypto_module: Crypto::CryptoModule.new_aes_cbc("enigma", true)
)
final pubnub =
PubNub(
...
// all necessary config options
crypto: CryptoModule.aescbcCryptoModule(CipherKey.fromUtf8('enigma'));
);
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"));
...
// all necessary config options
pnConfiguration.CryptoModule = new CryptoModule(new AesCbcCryptor("enigma"), new List<ICryptor> { new LegacyCryptor("enigma") })
new PubNub(pnConfiguration);
The raw message never leaves a trusted authority providing for full circle data encryption. It's encrypted at the time of publish by the PubNub SDK before it's sent and it remains encrypted as it's routed through the PubNub Network till it reaches the client that subscribes to it.
The subscribing client needs to decrypt it using the same cipher key. All data that is persisted in Message Persistence (and logging systems) is encrypted and can't be decrypted by anyone unless they hold the required cipher key.
Protect your cipher key
You shouldn't share the cipher key outside of the PubNub network.
Partial message encryption
Sometimes, only encrypting certain parts of your message makes more sense than encrypting the whole payload. If you're using Mobile Push Notifications and encrypt all messages and files, PubNub will not be able to read the mobile push keys and values that you provide in your message payload. In such cases, you need to encrypt only the sensitive data in your message payload while leaving other pieces of data as clear text.
It is best to create a separate crypto module instance and call the encrypt()
or decrypt()
methods on that instance.
APNs example
Let's use an example message payload to demonstrate the proper way to do precise and necessary message data encryption specific to an APNs payload.
Before encryption:
{
"pn_apns": {
"aps": {
"alert": "Your test results are available.",
"sound" : "bingbong.aiff"
}
},
"results": {
"test_name": "pregnancy",
"results": "positive",
"notes": "You are having twins!"
}
}
Encrypt it:
In the above message payload, the only data that really must be encrypted is the results
object. Everything else can remain as plain text.
- JavaScript
- Swift
- Objective-C
- Java
const clearText = JSON.stringify(
{"test_name":"pregnancy","results":"positive","notes":"You are having twins!"});
// create a crypto module instance
const cryptoModule = PubNub.CryptoModule.aesCbcCryptoModule({
cipherKey: "pubnubenigma"
});
const secureText = cryptoModule.encrypt(clearText);
let clearData = [
"test_name":"pregnancy",
"results":"positive",
"notes":"You are having twins!"
]
do {
let cryptoModule = CryptoModule.aesCbcCryptoModule(with: "pubnubenigma")
let encryptedMessage = try cryptoModule.encrypt(data: try JSONEncoder().encode(clearData)).get()
} catch {
// ...
}
PNCryptoModule *aesCBCCrypto = [PNCryptoModule AESCBCCryptoModuleWithCipherKey:@"enigma" randomInitializationVector:YES];
NSString *message = @"You are having twins!";
NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedWithAESCBC = [aesCBCCrypto encrypt:messageData];
JsonObject clearData = new JsonObject();
clearData.addProperty("test_name", "pregnancy");
clearData.addProperty("results", "positive");
clearData.addProperty("notes", "You are having twins!");
CryptoModule aesCbcCryptoModule = CryptoModule.createAesCbcCryptoModule("myCipherKey01", true);
byte[] encryptedData = aesCbcCryptoModule.encrypt(clearData.toString().getBytes(StandardCharsets.UTF_8));
You can now add the secureData
variable to the payload so that PubNub and third party mobile push vendors can process the Push payload correctly.
Using a cipher key, pubnubrealtimecommunicationplatf
in this example (your cipher key will be more complex, but exactly 32 chars are required for 256 AES cipher keys), to encrypt only the confidential data, the resulting message payload would be as follows.
After encryption:
{
"pn_apns": {
"aps": {
"alert": "Your test results are available.",
"sound" : "bingbong.aiff"
}
},
"results": "Nio2At2uvQzteLoSqznLkZQ3dlFlMzLGlUwbsMndZ7/7tllq2joJw6WcUv3XpMdEugYRnoEvsrlhEVkSnBibxaxDDWMFEDvKS+VLlA8mfmg="
}
Partial Message Encryption
You may need to encrypt certain data values within the APNs/FCM payload, but be sure not to encrypt any APNs/FCM keys, or any values that need to be parsed as unencrypted text by those mobile push services.
Decrypting messages
On the message receiver side, your client must also have the same module and cipher key to use the decrypt
API on the encrypted parts of the message payload.
- JavaScript
- Swift
- Objective-C
- Java
// parse the received message and pass the encrypted parts to decrypt API.
const secureText =
"Nio2At2uvQzteLoSqznLkZQ3dlFlMzLGlUwbsMndZ7/7tllq2joJw6WcUv3XpMdEugYRnoEvsrlhEVkSnBibxaxDDWMFEDvKS+VLlA8mfmg="
const clearData = pubnub.decrypt(secureText);
// parse the received message and pass the encrypted parts to decrypt API
let secureText =
"Nio2At2uvQzteLoSqznLkZQ3dlFlMzLGlUwbsMndZ7/7tllq2joJw6WcUv3XpMdEugYRnoEvsrlhEVkSnBibxaxDDWMFEDvKS+VLlA8mfmg="
do {
let clearData = try pubnub.decrypt(data: Data(base64Encoded: secureText) ?? Data()).get()
} catch {
// ...
}
PNCryptoModule *aesCBCCrypto = [PNCryptoModule AESCBCCryptoModuleWithCipherKey:@"enigma" randomInitializationVector:YES];
PNCryptoModule *aesCBCCrypto = [PNCryptoModule AESCBCCryptoModuleWithCipherKey:@"enigma" randomInitializationVector:YES];
// parse the received message and pass the encrypted parts to decrypt API
NSString *secureText =
@"Nio2At2uvQzteLoSqznLkZQ3dlFlMzLGlUwbsMndZ7/7tllq2joJw6WcUv3XpMdEugYRnoEvsrlhEVkSnBibxaxDDWMFEDvKS+VLlA8mfmg="
NSData *secureData = [[NSData alloc] initWithBase64EncodedString:secureText options:0];
NSString *clearData = [aesCBCCrypto decrypt:secureData];
// parse the received message and pass the encrypted parts to decrypt API.
String secureText =
"Nio2At2uvQzteLoSqznLkZQ3dlFlMzLGlUwbsMndZ7/7tllq2joJw6WcUv3XpMdEugYRnoEvsrlhEVkSnBibxaxDDWMFEDvKS+VLlA8mfmg="
JsonObject clearData = JSON.parse(pubnub.decrypt(secureText));
JsonReader jsonReader = Json.createReader(new StringReader(clearData));
clearData = jsonReader.readObject();
jsonReader.close();
For more examples of decrypting messages in other languages, refer to the Configuration and/or Miscellaneous documentation of each SDK.
File encryption
Much like messages, you can also encrypt the files sent from your clients using a crypto module.
If you registered a crypto module in the configuration, you don't have to explicitly specify it in the method to encrypt or decypt the uploaded files.
Do not pass cipher key
If you do pass a cipher key in the sendFile
or downloadFile
methods, it overrides the crypto module configuration and the legacy encryption with 128-bit cipher key entropy is used.
Refer to Encrypting Messages for information on how to configure the crypto module.
With client-side encryption, the SDK encrypts file data before it's uploaded to the storage service. The receiving client must decrypt the data upon download using the same key before it's displayed in the end-user application.
Encrypting files
The following example shows how to encrypt a file using the crypto module registered in PubNub config.
- Node.js
- C#
- Go
- Objective-C
- Java
import fs from 'fs';
const myFile = fs.readFileSync('./cat_picture.jpg');
const result = await pubnub.sendFile({
channel: 'my_channel',
message: 'Look at this photo!',
file: { data: myFile, name: 'cat_picture.jpg', mimeType: 'application/json' }
});
PNResult<PNFileUploadResult> fileUploadResponse = await pubnub.SendFile()
.Channel("my_channel")
.File("cat_picture.jpg") //checks the bin folder if no path is provided
.Message("Look at this photo!")
.ExecuteAsync();
PNFileUploadResult fileUploadResult = fileUploadResponse.Result;
PNStatus fileUploadStatus = fileUploadResponse.Status; // contains troubleshooting info
if (!fileUploadStatus.Error && fileUploadResult != null) // checks if successful
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileUploadResult));
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileUploadStatus));
}
file, err := os.Open("cat_picture.jpg")
defer file.Close()
if err != nil {
panic(err)
}
resSendFile, statusSendFile, errSendFile := pn.SendFile().
Channel("my_channel").
Message("Look at this photo!").
Name("cat_picture.jpg").
File(file).Execute()
fmt.Println(resSendFile, statusSendFile, errSendFile)
fmt.Println(resSendFile.Data.ID)
NSURL *localFileURL = ...;
PNSendFileRequest *request = [PNSendFileRequest requestWithChannel:@"my_channel"
fileURL:localFileURL];
[self.client sendFileWithRequest:request completion:^(PNSendFileStatus *status) {
if (!status.isError) {
/**
* File upload successfully completed.
* Uploaded file information is available here:
* status.data.fileIdentifier is the unique file identifier
* status.data.fileName is the name used to store the file
*/
} else {
/**
* Handle send file error. Check the 'category' property for reasons
show all 22 linespubnub.sendFile()
.channel("my_channel")
.fileName("cat_picture.jpg")
.inputStream(inputStream)
.message("Look at this photo!")
.async(result -> {
result.onSuccess(res -> {
System.out.println("send timetoken: " + res.getTimetoken());
System.out.println("send status: " + res.getStatus());
System.out.println("send fileId: " + res.getFile().getId());
System.out.println("send fileName: " + res.getFile().getName());
});
});
Decrypting files
The following example shows how to decrypt a file using the crypto module registered in PubNub config.
- Node.js
- C#
- Go
- Objective-C
- Java
const file = await pubnub.downloadFile({
channel: 'my_channel',
id: 'd9515cb7-48a7-41a4-9284-f4bf331bc770',
name: 'cat_picture.jpg'
});
PNResult<PNDownloadFileResult> fileDownloadResponse = await pubnub.DownloadFile()
.Channel("my_channel")
.FileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
.FileName("cat_picture.jpg")
.ExecuteAsync();
PNDownloadFileResult fileDownloadResult = fileDownloadResponse.Result;
PNStatus fileDownloadStatus = fileDownloadResponse.Status;
if (fileDownloadResult != null)
{
fileDownloadResult.SaveFileToLocal(downloadUrlFileName); //saves to bin folder if no path is provided
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileDownloadResult.FileName));
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileDownloadStatus));
show all 16 linesresDLFile, statusDLFile, errDLFile := pn.DownloadFile().
Channel("my_channel").
ID("d9515cb7-48a7-41a4-9284-f4bf331bc770").
Name("cat_picture.jpg").Execute()
if resDLFile != nil {
filepathOutput := "cat_picture.jpg"
out, _ := os.Create(filepathOutput)
_, err := io.Copy(out, resDLFile.File)
if err != nil {
fmt.Println(err)
}
}
PNDownloadFileRequest *request = [PNDownloadFileRequest requestWithChannel:@"my_channel"
identifier:@"<file-identifier>"
name:@"cat_picture.jpg"];
request.targetURL = ...;
[self.client downloadFileWithRequest:request
completion:^(PNDownloadFileResult *result, PNErrorStatus *status) {
if (!status.isError) {
/**
* File successfully has been downloaded.
* status.data.location - location where downloaded file can be found
* status.data.temporary - whether file has been downloaded to temporary storage and
* will be removed on completion block return.
*/
} else {
show all 23 linespubnub.downloadFile()
.channel("my_channel")
.fileName("cat_picture.jpg")
.fileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
.async(result -> {
result.onSuccess(res -> {
System.out.println("getFile fileName: " + result.getFileName());
System.out.println("getFile byteStream: " + result.getByteStream());
});
});
For more examples of encrypting/decrypting files in other languages, refer to the Configuration and/or Files documentation of each SDK.
Custom encryption
The message and file encryption/decryption functionality is decoupled from our SDKs, which allows you to provide your implementations of the methods responsible for encrypting and decrypting message and file data.
Each PubNub SDK exposes an interface to register a list of different cryptorsCryptor
An implementation of a specific cryptographic algorithm used for data encryption/decryption that adheres to a standard interface.
For decryption, any of the cryptors on the list may be applied depending on the one used when initially sending the message/file. The SDK inspects the incoming payload and decides which cryptor to use.
Implementation guidelines
The way to use custom encryption in PubNub SDKs differs across SDKs, as some of them provide file encryption and some don't. However, the general implementation flow remains the same:
-
Implement the
Crypto
andStreamingCrypto
interfaces (actual names vary) and provide concrete implementations for the methods they provide. Refer to the Crypto interfaces section to find out which interface you should implement. -
Create a new Crypto module instance with your cryptor implementation and pass it to PubNub configuration or use it standalone for partial encryption. Refer to each SDK's Crypto module documentation.
Example custom cryptor implementation
This example uses Kotlin, but the general idea is the same across all SDKs.
- Implement the
Cryptor
interface. Note that theid()
method should return aByteArray
of exactly 4 bytes.
fun myCustomCryptor() = object : Cryptor {
override fun id(): ByteArray {
// Should return a ByteArray of exactly 4 bytes.
return byteArrayOf('C'.code.toByte(), 'U'.code.toByte(), 'S'.code.toByte(), 'T'.code.toByte())
}
override fun encrypt(data: ByteArray): EncryptedData {
// implement your crypto logic
return EncryptedData(metadata = null, data = data)
}
override fun decrypt(encryptedData: EncryptedData): ByteArray {
// implement your crypto logic
return encryptedData.data
}
show all 26 lines- Use your newly created
myCustomCryptor
that implements theCryptor
interface to instantiate aCryptoModule
:
val customCryptor = myCustomCryptor()
val cryptoModule = CryptoModule.createNewCryptoModule(defaultCryptor = customCryptor)
Interfaces to implement
- C-Core:
pbcc_crypto.h
- C#:
ICryptor.cs
- Dart:
ICryptor.dart
- Go:
cryptor.go
- Java:
Cryptor.kt
- JavaScript:
ICryptor.ts
- Kotlin:
Cryptor.kt
- Objective-C:
PNCryptor.h
- PHP:
Cryptor.php
- Python:
crypto_core.py
- Ruby:
cryptor.rb
- Rust:
cryptor.rs
- Swift:
Cryptor.swift
- Unity:
ICryptor.cs