Securing Third‑Party APIs: AK/SK Generation, Signature Rules, and Anti‑Replay Strategies
This guide explains how to secure third‑party APIs by generating unique Access Key/Secret Key pairs, designing signature processes with timestamps and nonces, implementing permission division, creating robust API endpoints, and applying best‑practice security measures such as HTTPS, token handling, rate limiting, and idempotency.
Design Overview
1. API key generation : Generate a unique Access Key/Secret Key (AK/SK) pair for each third‑party application. The AK identifies the application, while the SK is used for signing and encryption.
AK (Access Key Id) is used to identify the user. SK (Secret Access Key) is the secret key for generating and verifying authentication strings and must be kept confidential.
2. Interface authentication : When calling an interface, the client uses the AK and request parameters to generate a signature, which is placed in the request header or parameters for identity verification.
3. Callback address setting : The third‑party application provides a callback URL to receive asynchronous notifications and results.
4. API interface design : Define the URL, HTTP method, request parameters, and response format for each endpoint.
Permission Division
appID : Unique identifier of the application.
appKey : Public key (account) used to call services.
appSecret : Private key (password) paired with appKey for signing.
token : Temporary token that expires.
When requesting authorization from the third‑party server, include appKey and appSecret (stored on the server).
The server verifies whether appKey and appSecret exist in the database or cache.
If they exist, generate a unique token string and return it to the client.
Subsequent client requests must include the token.
Why pair appKey and appSecret? The pair enables encryption during the initial verification (similar to a login scenario). The appKey indicates the requested permissions, and the appSecret acts as a password. Together they generate an accessToken with an expiration time, which must be presented on each request to prove valid permissions.
When a single appId needs different permission levels (e.g., read‑only vs. read‑write), a single appKey/appSecret pair is insufficient. Multiple appKey+appSecret pairs can be created, each assigned specific permissions.
Signature Process
The client assembles all request parameters (including appId, timestamp, nonce) except the signature itself, sorts them by key name in ascending order, concatenates them as key1value1key2value2…, appends the secret, and computes the MD5 hash (uppercase) to obtain the sign value.
Signature Rules
1. Assign a unique appId (developer identifier) and appSecret (key) to each caller.
2. Add a timeStamp (milliseconds) based on server time; the request is valid only within a short window (e.g., 5 minutes) to mitigate replay attacks.
3. Add a nonce (random string, at least 10 characters) to ensure each request is unique within the validity period.
4. Include a sign field generated from the sorted parameters, timestamp, nonce, and secret. The server recomputes the signature and compares it to the received value.
If the timestamp is older than the allowed window or the nonce has been seen before, the request is rejected as a replay attack.
API Interface Design
Examples of typical CRUD endpoints:
Get resource list : URL: /api/resources Method: GET Parameters: page (optional), limit (optional)
Create resource : URL: /api/resources Method: POST Parameters: name (required), description (optional)
Update resource : URL: /api/resources/{resourceId} Method: PUT Parameters: resourceId (path, required), name (optional), description (optional)
Delete resource : URL: /api/resources/{resourceId} Method: DELETE Parameters: resourceId (path, required)
Security Considerations
Use HTTPS for all data transmission.
Authenticate requests with AK and signature, and verify signatures on the server side.
Encrypt sensitive data during transmission (e.g., TLS).
AK and SK Generation Scheme
Design an API key management system to generate and store AK/SK pairs, similar to major cloud providers. Store the pairs in a database with fields such as app_id, access_key, secret_key, validity period, enabled flag, and allowed endpoints.
CREATE TABLE api_credentials (
id INT AUTO_INCREMENT PRIMARY KEY,
app_id VARCHAR(255) NOT NULL,
access_key VARCHAR(255) NOT NULL,
secret_key VARCHAR(255) NOT NULL,
valid_from DATETIME NOT NULL,
valid_to DATETIME NOT NULL,
enabled TINYINT(1) NOT NULL DEFAULT 1,
allowed_endpoints VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);API Design Supplement
Additional best practices:
Use POST for all API requests to avoid exposing parameters in URLs.
Configure client IP whitelist (or firewall rules) to restrict access.
Implement per‑IP rate limiting using Redis (key = ip+endpoint).
Log each request for troubleshooting.
Mask sensitive data (e.g., order numbers) using encryption such as RSA.
Ensure idempotency by providing a globally unique nonce for write operations and storing the result in Redis.
Version APIs (e.g., /v1/..., /v2/...) to allow backward‑compatible changes.
Standardize response status codes (200 success, 4xx client error, 5xx server error).
public enum CodeEnum {
SUCCESS(200, "处理成功"),
ERROR_PATH(404, "请求地址错误"),
ERROR_SERVER(505, "服务器内部发生错误");
private int code;
private String message;
CodeEnum(int code, String message) { this.code = code; this.message = message; }
public int getCode() { return code; }
public String getMessage() { return message; }
} public class Result implements Serializable {
private static final long serialVersionUID = 793034041048451317L;
private int code;
private String message;
private Object data = null;
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Object getData() { return data; }
public Result fillCode(CodeEnum codeEnum) {
this.setCode(codeEnum.getCode());
this.setMessage(codeEnum.getMessage());
return this;
}
public Result fillData(Object data) {
this.setCode(CodeEnum.SUCCESS.getCode());
this.setMessage(CodeEnum.SUCCESS.getMessage());
this.data = data;
return this;
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
