Security Best Practices
Protect your application and users with proper API key management, input validation, and secure data handling.
API Key Security
Public vs Secret Keys
The MIMS SDK uses two types of API keys:
pk_*- Public keys, safe for client-side use with limited scopessk_*- Secret keys, server-side only with full access
Never Expose Secret Keys
Secret keys (
sk_*) should NEVER be used in client-side code, committed to version control, or exposed in browser network requests.Safe API Key Usagetsx
// CORRECT: Public key in client-side code
<MIMSProvider apiKey={process.env.NEXT_PUBLIC_MIMS_API_KEY!}>
<App />
</MIMSProvider>
// WRONG: Never use secret keys client-side
// <MIMSProvider apiKey={process.env.MIMS_SECRET_KEY!}> // DANGER!
// CORRECT: Secret keys in server-side API routes only
// app/api/documents/route.ts
export async function POST(request: Request) {
const response = await fetch('https://api.mims-tech.com/documents', {
headers: {
'X-API-Key': process.env.MIMS_SECRET_KEY!, // Server-side only
},
});
return Response.json(await response.json());
}Environment Variables
.env.localbash
# Public key - safe for client-side (NEXT_PUBLIC_ prefix)
NEXT_PUBLIC_MIMS_API_KEY=pk_live_xxxxxxxxxxxx
# Secret key - server-side only (no NEXT_PUBLIC_ prefix)
MIMS_SECRET_KEY=sk_live_xxxxxxxxxxxx.gitignoretext
# Ignore all environment files
.env
.env.local
.env.*.localInput Validation
Always validate user input before processing or submission:
Input Validationtsx
import { z } from 'zod';
// Define validation schemas
const textFieldSchema = z.object({
type: z.literal('text'),
content: z.string()
.min(1, 'Field is required')
.max(1000, 'Maximum 1000 characters')
.refine(
(val) => !/<script/i.test(val),
'Invalid content detected'
),
});
const signatureFieldSchema = z.object({
type: z.literal('signature'),
imageData: z.string()
.startsWith('data:image/', 'Must be a valid image')
.max(500000, 'Image too large'), // ~500KB limit
});
// Validate before setting field value
function validateAndSetFieldValue(
fieldId: string,
value: unknown,
setFieldValue: (id: string, value: AnyFieldValue) => void
) {
const field = useDocumentStore.getState().fields.get(fieldId);
if (!field) return { success: false, error: 'Field not found' };
try {
let validated: AnyFieldValue;
switch (field.definition.type) {
case 'text':
validated = textFieldSchema.parse(value);
break;
case 'signature':
validated = signatureFieldSchema.parse(value);
break;
// ... other field types
default:
throw new Error('Unknown field type');
}
setFieldValue(fieldId, validated);
return { success: true };
} catch (error) {
if (error instanceof z.ZodError) {
return { success: false, error: error.errors[0].message };
}
return { success: false, error: 'Validation failed' };
}
}Content Security
XSS Prevention
Safe Renderingtsx
// WRONG: Rendering user content as HTML
function UnsafeLabel({ field }: { field: FieldState }) {
return <div dangerouslySetInnerHTML={{ __html: field.definition.label }} />;
}
// CORRECT: Render as text content
function SafeLabel({ field }: { field: FieldState }) {
return <div>{field.definition.label}</div>;
}
// If you must render HTML, sanitize it first
import DOMPurify from 'dompurify';
function SanitizedContent({ html }: { html: string }) {
const sanitized = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
ALLOWED_ATTR: [],
});
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}Content Security Policy
next.config.jstypescript
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'", // Required for PDF.js
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob: https://cdn.mims-tech.com",
"connect-src 'self' https://api.mims-tech.com",
"frame-src 'none'",
"object-src 'none'",
].join('; '),
},
];
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: securityHeaders,
},
];
},
};Secure Data Handling
Signature Data
Signature Securitytsx
// Clear signature data from memory after submission
async function submitAndCleanup() {
const payload = getSubmissionPayload();
try {
await submitDocument(payload);
// Clear sensitive data from store
useDocumentStore.getState().reset();
// Clear any canvas elements
document.querySelectorAll('canvas').forEach(canvas => {
const ctx = canvas.getContext('2d');
ctx?.clearRect(0, 0, canvas.width, canvas.height);
});
} catch (error) {
// Don't clear on error - user may need to retry
throw error;
}
}
// Don't log signature data
function logSubmission(payload: DocumentSubmissionPayload) {
const sanitized = {
...payload,
fields: payload.fields.map(f => ({
...f,
value: f.type === 'signature'
? { type: 'signature', imageData: '[REDACTED]' }
: f.value,
})),
};
console.log('Submission:', sanitized);
}Local Storage
Storage Securitytsx
// DON'T store sensitive data in localStorage
// localStorage.setItem('apiKey', apiKey); // NEVER do this
// If you must cache document data, exclude signatures
function cacheDocumentMetadata(doc: DocumentWithFields) {
const cacheable = {
metadata: doc.metadata,
fieldIds: doc.fields.map(f => f.id),
// Don't cache field values or signatures
};
sessionStorage.setItem(
`doc_${doc.metadata.id}`,
JSON.stringify(cacheable)
);
}
// Clear session data on logout
function clearSensitiveData() {
sessionStorage.clear();
useDocumentStore.getState().reset();
}Network Security
HTTPS Enforcement
HTTPS Checktsx
// Verify HTTPS in production
if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined') {
if (window.location.protocol !== 'https:') {
console.error('MIMS SDK requires HTTPS in production');
// Optionally redirect to HTTPS
window.location.href = window.location.href.replace('http:', 'https:');
}
}Request Integrity
Request Securitytsx
// The SDK automatically includes integrity headers
// You can add additional security headers if needed
<MIMSProvider
apiKey={process.env.NEXT_PUBLIC_MIMS_API_KEY!}
customHeaders={{
'X-Request-ID': generateRequestId(),
'X-Client-Version': packageJson.version,
}}
>
<App />
</MIMSProvider>Security Checklist
Before Going to Production
- Using public keys (
pk_*) in client-side code only - Secret keys are only used server-side
- Environment variables are not committed to git
- All user input is validated before processing
- HTTPS is enforced in production
- Content Security Policy headers are configured
- Sensitive data is not logged or stored in localStorage
- Error messages don't expose internal details
Security Reports
If you discover a security vulnerability, please email security@mims-tech.com. Do not disclose security issues publicly.