Quixxi Plugin
The Flutter plugin provides the following security enhancements for mobile applications:
SSL Pinning
-
Ensures secure communication between the application and backend servers by validating the server certificate against a pinned public key.
-
Protects against man-in-the-middle (MITM) attacks by preventing connections to unauthorized servers.
Clipboard Protection
-
Prevents copy and paste actions inside the application to safeguard sensitive information such as passwords, tokens, or personal data.
-
Reduces the risk of data leakage through clipboard monitoring or malicious apps.
API Attestation
-
Protects backend APIs from misuse by validating requests through Secure HashKey attestation.
-
Ensures that only genuine, untampered applications can communicate with the server.
-
Helps prevent unauthorized API calls and mitigates replay or injection attacks.
Integration Notes
Important
Quixxi Security Shield must be applied after integrating this plugin into your application.
SSL Pinning
-
Debug Builds
-
SSL certificate validation is not enforced.
-
This is intentional to simplify development and testing.
-
-
Release Builds
-
Quixxi Shield must be applied.
-
Enforces SSL Pinning and strong runtime protections.
-
API Attestation
- In Debug Mode, a default hardcoded hash key is used:
MNaiMYHqVH3ZLVfGVc4nqFcd7Gf8Avr6JePPUJZYfrCA7A00GiRSFTPC981P7Vpe1gDUh9YQaKEE2ihGp04GfW7EB2HJx8JVkwv5k1fvDhaQNMetvaFACAw70gP61KYK
-
Use this key only for development and testing.
-
In Production Builds, configure and use SecureHash Key generated on Quixxi portal to protect against API misuse.
Getting Started
Add quixxi as a dependency in your pubspec.yaml:
dependencies:
quixxi: 1.0.0
Usage example
SSL Pinning
Using Dio
import 'package:quixxi/services.dart';
Dio dioClient = getDioClient('https://gorest.co.in',enableSSLPinning: true);
void apiCall() async {
var header = {'Content-type': 'application/json; charset=utf-8'};
try {
final response = await dio.get("https://yourdomain.com/yourapi",
options: Options(headers: header));
if (response.statusCode == 200) {
apiResponse.value = response.data.toString();
} else {
print('${response.statusCode} : ${response.data.toString()}');
apiResponse.value = response.data.toString();
}
} catch (error) {
apiResponse.value = error.toString();
}
}
Using Http
import 'package:quixxi/services.dart';
SecureHttpClient secureHttpClient = getSecureHttpClient(enableSSLPinning: true);
void apiCall2() async{
var header = {'Content-type': 'application/json; charset=utf-8'};
try {
Uri uri = Uri.parse("https://gorest.co.in/public/v2/todos");
final response = await secureHttpClient.get(uri, headers: header);
if (response.statusCode == 200) {
apiResponse.value = response.body.toString();
}else{
print('${response.statusCode} : ${response.body.toString()}');
apiResponse.value = response.body.toString();
}
} catch (error) {
apiResponse.value = error.toString();
}
}
Custom Implementation
import 'package:quixxi/services.dart';
Future myCustomImplementation(String url, Map<String,String> headers) async {
try{
final secure = await SSLCertificatePinning().check(
serverURL: url,
headerHttp: headers,
timeout : 50
);
if(secure.contains("CONNECTION_SECURE")){
return true;
}else{
return false;
}
}catch(e){
return false;
}
}
API Attestation
You can enable API Attestation to automatically send a secure hash (from Quixxi) either in the request header or payload.
With SSLPinning
Using Dio
import 'package:quixxi/services.dart';
// Enable App Attestation and configure hash injection
Dio dioClient = getDioClient(
'https://gorest.co.in',
enableSSLPinning: true,
enableAppAttestation: true,
sendHashInHeader: true, // or false
sendHashInPayload: false, // or true
);
void apicall_dio_app_attestation() async {
var header = {'Content-type': 'application/json; charset=utf-8'};
try {
final response = await dioClient.get(
urlTextEditingController.text,
options: Options(headers: header),
);
apiResponse.value = response.data.toString();
} catch (error) {
apiResponse.value = error.toString();
}
}
Using Http
import 'package:quixxi/services.dart';
SecureHttpClient secureHttpClient = getSecureHttpClient(
enableSSLPinning: true,
enableAppAttestation: true,
sendHashInPayload: true, // or sendHashInHeader: true
);
void apicall_http_app_attestation() async {
var header = {'Content-type': 'application/json; charset=utf-8'};
try {
Uri uri = Uri.parse(urlTextEditingController.text);
final response = await secureHttpClient.get(uri, headers: header);
apiResponse.value = response.body.toString();
} catch (error) {
apiResponse.value = error.toString();
}
}
Without SSLPinning
Same usage, but dont pass enableSSLPinning. it is set to false by default.
SOAP API Support
App Attestation also works with SOAP requests. Just set "Content-Type": "text/xml; charset=utf-8" in headers and provide your envelope.
Using Dio
String soapEnvelope({String? bodyContent}) => '''
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
${bodyContent ?? ''}
</soap:Body>
</soap:Envelope>
''';
// Enable App Attestation and configure hash injection
Dio dioClient = getDioClient(
'https://httpbin.org',
enableAppAttestation: true,
sendHashInHeader: true, // or false
sendHashInPayload: false, // or true
);
void apicall_dio_soap_with_header() async {
try {
isLoading.value = true;
String envelope = soapEnvelope(bodyContent: '<Test>Hello</Test>');
final response = await dioClient.post(
"https://httpbin.org/post",
data: envelope,
options: Options(
headers: {
'Content-Type': 'text/xml; charset=utf-8',
'SOAPAction': 'urn:TestAction'
},
),
);
isLoading.value = false;
apiResponse.value = response.data.toString();
} catch (error) {
isLoading.value = false;
apiResponse.value = error.toString();
}
}
Using Http
String soapEnvelope({String? bodyContent}) => '''
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
${bodyContent ?? ''}
</soap:Body>
</soap:Envelope>
''';
// Enable App Attestation and configure hash injection
SecureHttpClient secureHttpClient = getSecureHttpClient(
enableAppAttestation: true,
sendHashInPayload: true, // or sendHashInHeader: true
);
void apicall_http_soap_with_payload() async {
try {
isLoading.value = true;
// Inject SecureHash inside <soap:Body>
String envelope = soapEnvelope(
bodyContent: '<Test>Hello</Test>',
);
Uri uri = Uri.parse("${urlTextEditingController.text}/post");
final response = await secureHttpClientWithHashInPayload.post(
uri,
headers: {
'Content-Type': 'text/xml; charset=utf-8',
'SOAPAction': 'urn:TestAction',
},
body: envelope,
);
isLoading.value = false;
apiResponse.value = response.body;
} catch (error) {
isLoading.value = false;
apiResponse.value = error.toString();
}
}
Copy & Paste Prevention
Secure Text Field
import 'package:flutter/material.dart';
import 'package:quixxi/text_field/quixxi_text_form_field.dart';
class ExamplePage extends StatelessWidget {
const ExamplePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Quixxi Example')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Quixxi Textfield'),
Container(
padding: const EdgeInsets.symmetric(vertical: 15),
Container(
padding: const EdgeInsets.symmetric(vertical: 15),
child: QuixxiTextField(
decoration: const InputDecoration(
border: UnderlineInputBorder(),
hintText: 'https://yourdomain.com',
),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.next,
controller: TextEditingController(
text: "https://yourdomain.com"),
),
),
),
],
),
),
);
}
}
Secure Text Form Field
import 'package:flutter/material.dart';
import 'package:your_package/quixxi_text_form_field.dart'; // adjust the import
class ExamplePage extends StatelessWidget {
const ExamplePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Quixxi Example')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Quixxi TextFormfield'),
Container(
padding: const EdgeInsets.symmetric(vertical: 15),
child: QuixxiTextFormField(
decoration: const InputDecoration(
border: UnderlineInputBorder(),
hintText: 'https://yourdomain.com',
),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.next,
controller: TextEditingController(
text: "https://yourdomain.com",
),
onSaved: (value) {},
validator: (value) {},
),
),
],
),
),
);
}
}
Configuration Parameters
When creating a Dio client with getDioClient or an Http client with getSecureHttpClient, you can configure the following options:
| Parameter | Type | Default | Description |
|---|---|---|---|
enableSSLPinning |
bool |
false |
Enables SSL certificate pinning (requires Quixxi Shield in release build). |
enableAppAttestation |
bool |
false |
Enables App Attestation, which sends a secure hash from Quixxi. |
sendHashInHeader |
bool |
false |
If true, the secure hash is sent in the HTTP/SOAP headers. |
sendHashInPayload |
bool |
false |
If true, the secure hash is injected into the request payload (JSON or SOAP <soap:Body>). |
baseUrl (Dio only) |
String |
— | Base URL for all API calls made with Dio client. |
Adding Quixxi Shield to application
- Create an account in https://portal.quixxi.com
- Create application container in your account.
- Upload your application to portal for app protection.
- For SSL Pinning, choose the option SSL certificate validation via SSLPinning in Shield configuration
- For API Attestation, choose the option API attestation in Shield configuration
- After applying shield configurations, wait for Shielding to complete
- Download protected application from portal.
Libraries
- quixxi_shield_plugin_method_channel
- quixxi_shield_plugin_platform_interface
- services
- sslpinning/dio/certificate_pinning_interceptor
- sslpinning/exceptions/exceptions
- sslpinning/http_client/secure_http_client
- sslpinning/ssl_certificate_pinning
- sslpinning/utils/encryption
- text_field/quixxi_text_form_field