"Please make sure internal materials do not get out."
Security requirements usually arrive in this form. The direction is clear, but this alone does not determine what has to be built.
If you take the request at face value and set the goal as "a system that completely prevents leaks," the project becomes difficult. The reason is that you cannot stop someone from photographing what is shown on a screen with another device. If you start without acknowledging this, the acceptance phase turns into an argument along the lines of "you said it would be blocked completely."
The realistic goal is different: reduce the number of leak paths, raise the cost of the paths that remain, and make incidents traceable when they happen. This article describes how to turn document security requirements into a system design from that perspective.
1. Break down what to block by leak path
"Leak prevention" is not a single feature but a combination of several controls. Design becomes possible only after the paths are separated.
Each path needs a different control
| Leak path | Control | Completeness |
|---|---|---|
| Viewing by unauthorized users | Authentication and authorization, access control | High |
| Taking a file out after download | Download restrictions, view-only viewer | Medium |
| Taking printouts out | Print control, watermark | Medium |
| Screen capture | Capture blocking (limited), watermark | Low |
| Photographing with another device | Watermark | Cannot be blocked; tracing only |
| Transcribing or memorizing the content | None | Cannot be controlled |
The further down the table you go, the harder blocking becomes. The core of the design is to block the upper paths reliably and to leave a means of tracing for the lower ones.
State completeness in the requirements
It is a good idea to review this table together with the client and agree on what will be done, and how far, for each path. That agreement later becomes the acceptance criteria.
"Screen capture blocking" needs particular care. Whether it is possible depends on the operating system and browser environment, and workarounds exist. Unless the feasible scope is written down clearly, expectations and results will diverge.
Write what was agreed as statements that can be tested. Below are examples of how to rephrase requests.
| Request received | Statement that can be accepted against |
|---|---|
| People without permission must not see it | When an account without permission requests the list, detail, file, thumbnail, or search results, every request is denied and the denial is recorded |
| Block downloads | For confidential-grade documents, no path returns a response that downloads the original file. Viewing is provided only as images converted by the server |
| Block capture | In the dedicated viewer (on the designated operating systems), the capture exclusion feature provided by the operating system is applied. Web browsers and photography with external devices are not subject to blocking and are traced by watermark |
| Make it possible to find the source if something leaks | The identification code on the screen or printout can be used to look up the viewer, the date and time, and the document. The scope of handling for cases where only part of the code is visible is defined separately |
Classify the documents by grade
Applying the same controls to every document paralyzes work. It is more realistic to define grades and set the level of control for each.
- Public — no controls
- Internal — viewable only by logged-in employees
- Restricted — viewable only by designated departments and positions; downloads restricted
- Confidential — individual approval required, view-only, watermark, full history recorded
You also need to decide who assigns the grade and by what procedure. Options include having the author designate it, applying a default grade per department, or applying it automatically when a document is saved to a particular location.
Grade change history must also be recorded. An approval procedure is often put in place so that lowering the grade of a confidential document does not become a path for taking it out.
2. Design the permission model
Access control is the area where blocking is most reliable. If there is a gap here, the remaining controls are meaningless.
Combine role-based and document-based control
| Approach | How it works | Pros and cons |
|---|---|---|
| Role-based | Permissions granted by position and department | Simple to manage. Exceptions are hard to handle |
| Document-based | Viewers designated per document | Precise. Heavy management burden |
| Hybrid | Roles by default, individual designation when needed | Realistic in most cases |
What has to be decided in the hybrid approach is whether an individual designation can exceed role permissions. You must decide whether to allow someone who normally has no permission to view a specific document, and conversely whether to exclude a specific document from someone who does have permission.
In terms of access control models, the distinction in the table is the difference between role-based (RBAC) and attribute-based (ABAC) access control. NIST defines ABAC as a method that decides whether to permit a request by evaluating the attributes of the subject, the object, the requested operation, and in some cases the environment conditions against policy, and describes RBAC as a special case of it that uses a single attribute, the "role."(NIST SP 800-162)
Most document security requirements cannot be expressed with roles alone. Conditions such as "confidential grade only on the internal network," "only during the project period," or "only if recently re-authenticated" can be decided only by looking at the document grade (an object attribute) together with the access location and time (environment conditions). The resulting structure sets the broad scope by role and narrows it by grade and environment conditions. There is no need to introduce a general-purpose policy engine from the start, but the attributes used in the decision must be fixed as a list. Which system each attribute comes from (HR data, document metadata, access information) and how current its value is determine the accuracy of the decision.
Fix the decision order in one place
Defects in the hybrid approach usually surface when rules conflict. If which side wins is implemented differently from screen to screen, for example when a role allows access but the document excludes the user, or when an individual grant exists but has expired, gaps appear. Define the order, put it in a single function, and make every access path go through that function.
Below is an example showing the decision order (TypeScript; the 5-minute re-authentication window is an example value). Attribute lookup is finished by the caller, and this function only makes the decision.
type Grade = 'PUBLIC' | 'INTERNAL' | 'RESTRICTED' | 'CONFIDENTIAL';
type Action = 'VIEW' | 'DOWNLOAD' | 'PRINT';
type DenyReason =
| 'ACCOUNT_INACTIVE' | 'EXPLICIT_DENY' | 'NO_MATCHING_RULE'
| 'ACTION_NOT_ALLOWED_FOR_GRADE' | 'REAUTH_REQUIRED';
type Decision = { allowed: true } | { allowed: false; reason: DenyReason };
interface AccessRequest {
isAccountActive: boolean;
action: Action;
grade: Grade;
isExplicitlyDenied: boolean; // per-document exclusion
individualGrantExpiresAt: number | null; // expiry time of the individual grant (epoch seconds)
isAllowedByRole: boolean; // whether a role/department rule matches
lastAuthenticatedAt: number; // time of last authentication (epoch seconds)
now: number;
}
const REAUTH_WINDOW_SECONDS = 300;
function deny(reason: DenyReason): Decision {
return { allowed: false, reason };
}
export function decideAccess(request: AccessRequest): Decision {
if (!request.isAccountActive) return deny('ACCOUNT_INACTIVE');
if (request.isExplicitlyDenied) return deny('EXPLICIT_DENY');
const hasValidGrant =
request.individualGrantExpiresAt !== null &&
request.individualGrantExpiresAt > request.now;
const isPublic = request.grade === 'PUBLIC';
if (!isPublic && !request.isAllowedByRole && !hasValidGrant) {
return deny('NO_MATCHING_RULE');
}
if (request.grade !== 'CONFIDENTIAL') return { allowed: true };
if (request.action !== 'VIEW') return deny('ACTION_NOT_ALLOWED_FOR_GRADE');
const isRecentlyAuthenticated =
request.now - request.lastAuthenticatedAt <= REAUTH_WINDOW_SECONDS;
return isRecentlyAuthenticated ? { allowed: true } : deny('REAUTH_REQUIRED');
}
There are four things to keep in this structure.
- Deny when no rule matches. When a new grade or a new action is added, access does not open even if a rule is missed.
- Exclusion comes before allowance. If the order is reversed, a single individual grant defeats the exclusion.
- Compare expiry at decision time. If you rely only on a batch job to delete expired permissions, the permissions stay alive while the batch is stopped.
- Return the deny reason as a value. This value becomes the failure reason in the audit log and the basis for telling the user why they were blocked. However, the wording shown on screen is defined separately from the reason value, so that a user without permission is not told whether the document exists.
Manage the permission lifecycle
With permissions, revocation is missed more often than granting.
- Are permissions on the previous department's documents retained after a department transfer?
- Is access blocked immediately on resignation?
- Do temporary permissions expire after the project ends?
- Do external partner accounts have a validity period?
It is safe to make an expiry date mandatory for temporary permissions. Permissions granted without expiry are never revoked by anyone and keep accumulating.
A procedure for reviewing permission status periodically is also needed: extract a list of who can currently access which documents and have an administrator review it.
If the document system holds personal information and therefore qualifies as a personal information processing system, this part is an obligation, not a recommendation. Article 5 of the Standards for Ensuring the Safety of Personal Information (개인정보의 안전성 확보조치 기준) requires that access rights be granted differentially within the minimum scope necessary to perform the work, that rights be changed or cancelled without delay when the person in charge or the work changes, and that records of granting, changing, and cancelling rights be kept for at least 3 years.(Standards for Ensuring the Safety of Personal Information) This is why the permission change history table must be kept separate from access records and given its own retention period.
One technical cause of delayed revocation is also worth noting. If permission decision results or user attributes are held in sessions, tokens, or caches, access continues until the cache expires even after the resignation has been processed in the HR system. For confidential-grade decisions, either do not use a cache or build a path that immediately invalidates the user's sessions and caches when permissions change.
Match the authentication method to the grade
Requiring additional authentication for confidential-grade documents is a common configuration.
- Authenticate once more at the time of viewing
- Apply multi-factor authentication
- Restrict access location and device
- Restrict access outside working hours
When you impose restrictions, you must build an exception handling path along with them. Cases where access is needed during a business trip or in an emergency will certainly arise. With an exception approval procedure and its records, work remains possible while control is maintained.
Separate download from viewing
The moment you let a file be downloaded, you lose control over that file. A file stored on a user's device can be moved anywhere.
To keep control, you need a path for viewing without downloading.
| Approach | How it works | Notes |
|---|---|---|
| Server-rendered viewer | Converted to images on the server and displayed | The original is not sent down. Conversion load |
| Web viewer | Displayed in the browser while blocking access to the original | The original can be exposed depending on the implementation |
| Dedicated application | Viewable only with a designated program | Installation burden. Many control options |
There is a point to watch when using a web viewer. To display a document in the browser, the data is delivered to the client in one form or another. If the structure allows the original to be extracted through developer tools or network records, there is no control.
Converting to images on the server and sending those down is free of this problem. The drawback is that text selection and search become impossible, but it is an appropriate trade-off for confidential-grade documents.
Where downloads must be allowed, conditions such as a limit on the number of downloads, a validity period, and entry of a reason are used. This is not complete control, but it reduces indiscriminate removal.
Verify on the server
This is obvious, yet it is a part that often goes wrong in practice.
- Hiding a button on the screen is not control
- A structure in which the list query filters documents but the detail query needs only an identifier is common
- The file download path is sometimes left open separately
- Preview images and thumbnails sometimes do not go through a permission check
The same permission check must be performed on every access path. In particular, if the URL of the file itself is exposed, the permission check can be bypassed, so the design must not leave any directly accessible path.
Sign file URLs and make them expire
Having the application server stream files directly keeps the permission check simple, but the load on the server is heavy for large files. A common configuration is therefore to let the storage or a dedicated file path respond directly, while the application checks permission and then issues a signed URL that is valid only for a short time.
The principle is to bind the document, the user, and the expiry time together, sign them with a key that only the server knows, and check the signature and expiry again when a request arrives. Below is an example for implementing this yourself (Node.js 16 or later, node:crypto).
import { createHmac, timingSafeEqual } from 'node:crypto';
interface DownloadClaim {
documentId: string;
userId: string;
expiresAt: number; // epoch seconds
}
function sign(claim: DownloadClaim, secret: string): string {
const payload = JSON.stringify([claim.documentId, claim.userId, claim.expiresAt]);
return createHmac('sha256', secret).update(payload).digest('base64url');
}
export function createDownloadPath(claim: DownloadClaim, secret: string): string {
const query = new URLSearchParams({
u: claim.userId,
e: String(claim.expiresAt),
s: sign(claim, secret),
});
return `/api/documents/${encodeURIComponent(claim.documentId)}/file?${query}`;
}
interface VerifyInput {
claim: DownloadClaim; // values read again from the request path and query
signature: string;
secret: string;
now: number;
}
export function isValidDownloadRequest(input: VerifyInput): boolean {
const { claim, signature, secret, now } = input;
if (!Number.isInteger(claim.expiresAt) || claim.expiresAt <= now) return false;
const expected = Buffer.from(sign(claim, secret));
const given = Buffer.from(signature);
return expected.length === given.length && timingSafeEqual(expected, given);
}
The nature is the same when you use the signed URL feature of a cloud storage service. AWS describes an S3 presigned URL as "a bearer token that grants access to those who possess it" and notes that it can be used multiple times until it expires.(Amazon S3 User Guide) A signed URL does not replace the permission check; it is a token that carries the result of a permission check around for a short time. The following are therefore designed together with it.
- Keep expiry short, in minutes. The moment a URL is pasted into a messenger, it passes to someone else. The longer the expiry, the later a permission revocation takes effect. For S3, the same document describes how to deny requests made a certain time after signing with a bucket policy (the
s3:signatureAgecondition) and how to restrict the networks from which access is possible. - Expiry is checked only when the request starts. According to the same document, S3 checks expiry at the time of the HTTP request, so a download started just before expiry continues after expiry. The expiry time must not be written in the requirements as "the time after which the file is no longer transferred."
- Include the user in the signature, and on paths that have a session, compare it with the session user. In a configuration where the storage responds directly, the comparison is not possible, so set the expiry even shorter.
- Write an audit log entry at the time of issue. When the storage responds directly, the application cannot know about the actual download. Make it possible to link the issue records and the storage access logs by document identifier.
- Do not use them for the confidential grade. For a grade that has no path at all through which the original is sent down, signed URLs are not issued either.
- Make the signing key replaceable. If a key identifier is included in the URL, URLs issued with the previous key can be verified until their expiry time while the key is being rotated.
Common errors in permission design
| Symptom | Cause | Fix |
|---|---|---|
| A document not in the list opens when its address is entered directly | Only the list query has the permission condition; the detail and file APIs check only the identifier | Put the decision function in a common layer and make every API that returns documents go through it. Include automated tests that call each API with an unauthorized account as a deployment condition |
| The body is blocked, but the title and part of the body appear in search results | The search index was built without permission information | Put grade and permission attributes in the index so results are filtered at the search stage, and decide again before returning results |
| Thumbnail and preview images open without login | Conversion outputs are stored on a public path or CDN | Treat conversion outputs at the same grade as the original and serve them through the same decision |
| Viewing works for a while with a resigned employee's account | Permissions and attributes are kept in sessions or caches with no invalidation path | Invalidate sessions and caches immediately when permissions change; decide on every request for higher grades |
| The previous user's document is visible via the back button or on a shared PC | Document responses are cached by the browser or an intermediate server | Apply Cache-Control: no-store to document and image responses |
| A temporary permission still works after its expiry date | Expiry handling relies only on a deletion batch | Compare the expiry time at decision time |
| Every document opens in the administrator screen | Administrative functions do not go through the decision function | Separate system administration permission from document viewing permission, and make administrator viewing go through the same decision and recording |
3. A watermark is a means of tracing, not blocking
Expectations of watermarks often diverge from their actual effect.
What you can expect
A watermark overlays viewer information on the screen or printout. Two effects can be expected.
- Deterrence — leaking a document stamped with your own name is uncomfortable
- Traceability — if leaked material is found, you can identify whose viewing it came from
It has no blocking effect. Photographing and capturing remain possible even with a watermark. This must be made clear at the requirements stage.
Decide what to display
Tracing accuracy depends on what is put in the watermark.
- Viewer name and affiliation
- Employee number or account identifier
- Date and time of viewing
- Access IP or device information
- Document identifier
The more information you include, the easier tracing becomes, but readability suffers. In practice, the approach is to display the minimum on screen and keep the details as server records: show only a short identification code on the screen and look up the viewing record with that code.
There are three things to decide when using an identification code.
- Unit of the code — if a new code is issued for each viewing, it narrows down "who, when, and which document" in one step. A fixed code per user is easy to implement, but the date and time and the document have to be found from other clues.
- How the code is generated — a random value issued by the server is better than a value derived directly from the employee number or account name. Personal information is not carried directly on leaked material, and nobody can guess another person's code from looking at a code.
- Retention period of the code and the records — if the viewing records to be looked up by the code are deleted first, the watermark becomes an unreadable mark. Align the period during which watermark tracing must be possible with the audit log retention period.
Decide how to display it
- Position — fixed, or moving at regular intervals. Moving makes it harder to capture with a specific part covered
- Opacity — a level at which the document can be read while the mark remains identifiable when photographed
- Density — repeated across the whole screen, or shown only in part
- Application when printing — whether to apply it to the screen and to printouts respectively
Printout watermarks must be designed separately. Screen watermarks are handled in the browser, but printing is handled at the output stage, so a different method is needed. Generating a file with the watermark inserted on the server at print time and sending that down is the reliable approach. The density must also be set separately from the screen. In the watermark DRM solution we built, there was also the problem that a dark mark made the document hard to read while a light one disappeared in the copying process, so we had to adjust density and placement to find a balance.
You also need to decide where the screen watermark is composited. Having the browser draw it as a separate element over the document is simple to implement, but because the watermark and the document exist separately on the user's device, turning the mark off is not difficult. For higher-grade documents, the server draws the watermark in when it converts pages to images, so that screen data without a watermark is never delivered to the client. In this case the image differs for each viewer, so conversion results cannot be shared in a cache across users, and the conversion load must be reflected in capacity sizing accordingly.
Cases where a watermark does not hold up
A watermark is a means of tracing only when it remains on the leaked material and can be read. Work through the following situations in advance at the design stage.
| Situation | Result | What the design can do |
|---|---|---|
| A capture or photo of only part of the screen | Only an area without the watermark may leak | Repeat it across the whole screen, with spacing designed so that the full code appears at least once wherever a screen is cropped |
| Low-resolution photography, downscaling, recompression | A faint watermark is smeared and unreadable | Run readability tests under actual photography and downscaling conditions and set the density and font size from the results |
| Retyping or summarizing the content and passing it on | The watermark does not follow | No technical means. Narrowing the scope with viewing records is the limit |
| The same document viewed by several people | For leaked material without a watermark, every viewer is a candidate | Narrowing who can view is the way to narrow the scope of tracing |
The second item is worth including as an acceptance item. If you only confirm that "the watermark is displayed" and move on, you find out whether the code can actually be read from a photo only after an incident. Running one test in which the code is read from a photo taken of the target monitor with an ordinary smartphone is enough.
Marks can also be embedded in the document itself
Separately from visual watermarks, there is a method of inserting identification information into the document file itself. It is not visible to the naked eye but can be confirmed through analysis.
Points to review when applying this method:
- Can it be applied to each file format?
- Does it survive editing and re-saving?
- What tool can be used to check it?
- Has leaving personal information in the file been reviewed?
The last item is important. Identification information remaining in the file means that when the file goes outside, personal information goes with it. What information to include must be decided carefully. Here too, inserting an identification code issued by the server instead of a name or employee number is the safe approach.
An invisible mark cannot replace a visible watermark, because their roles differ.
| Category | Visible watermark | Invisible mark |
|---|---|---|
| Deterrence | Yes. The viewer is aware of the mark | No. There is no deterrence unless its existence is made known |
| How it is checked | By eye | A dedicated detection tool and the original or a key |
| Weak point | Cropping, covering | Whether it survives format conversion, re-saving, and photography differs by method |
| Suitable use | Screen viewing, printouts | Verifying the origin of documents taken out as files |
Whether an invisible mark can still be detected after conversion, printing, or photography varies greatly by method and product, and the conditions presented by the vendor can differ from actual usage conditions. If you are considering adoption, define the target file formats and the expected leak paths and run detection tests yourself, and record the paths where the mark is not detected under the limitations item of the requirements document. If you plan to use detection results as grounds for disciplinary action or legal proceedings, the possibility of false detection and the method of recording the detection procedure are also subject to review.
4. Design the audit log
If there are no records to check when a leak occurs, the controls described above lose their meaning.
First check the level required by law
For a system that handles personal information, requirements on access records apply. The content of Article 8 of the current Standards for Ensuring the Safety of Personal Information (Personal Information Protection Commission (개인정보보호위원회) Notice No. 2025-9, partially amended October 31, 2025) is as follows.(Standards for Ensuring the Safety of Personal Information)
| Provision | Content |
|---|---|
| Article 8(1) | Keep and manage the access records of persons who accessed the personal information processing system (excluding data subjects) for at least 1 year |
| Article 8(1), proviso | At least 2 years if any of the following applies: a system that processes personal information on 50,000 or more data subjects, a system that processes unique identification information or sensitive information, or a facilities-based telecommunications business operator (기간통신사업자) |
| Article 8(2) | Define in the internal management plan, and carry out, the cycle and method for checking and inspecting access records and the status of personal information downloads, and the follow-up action procedure |
| Article 8(3) | Measures to keep access records safely so that they are not forged, altered, stolen, or lost |
Article 2 of the same Notice defines access records as an electronic record of the identifier, the date and time of access, access location information, information on the data subjects processed, the work performed, and so on. Applied to a document system, the scope of recording covers who, when, from where, a document containing whose information, and whether it was viewed or downloaded.
Pay attention to the effective date. The Addenda to Notice No. 2025-9 provide that the amended provisions of Article 8(1) and (2), Article 5(1) and (6), and others take effect one year after the date of issuance, that is, from October 31, 2026. There are two differences from the previous provisions (Notice No. 2023-6) that apply until then.
- Scope of retention — previously it was the access records of "personal information handlers," but the amended provisions broadened it to "persons who accessed the personal information processing system (excluding data subjects)." You need to check that the records of those who are not internal employees, such as partner company accounts, are not missing.
- Inspection cycle — previously access records had to be inspected at least once a month, but the amended provisions changed this so that the cycle and method are defined in the internal management plan. Rather than a fixed feature such as a "monthly inspection report," the system needs a feature that extracts the inspection targets according to the defined cycle and records the inspection results and follow-up actions.
The retention period is a lower bound. On the other hand, viewing records are themselves the personal information of employees, so accumulating them indefinitely without grounds is not appropriate either. Define the retention periods for access records (at least 1 or 2 years) and for permission change records (Article 5(3), at least 3 years) separately, and design the procedure for deleting records past their period along with them.
For the interpretation of the provisions and application cases, you can refer to the guide published by the Personal Information Protection Commission. However, the guide at the link below is the October 2024 edition and is based on the content before the Notice No. 2025-9 amendment, so for the amended provisions the text of the Notice must be checked first.(Guide to the Standards for Ensuring the Safety of Personal Information)
The scope of application and the specific requirements vary depending on the type and scale of the personal information processed. Whether the document system qualifies as a personal information processing system has to be determined first, and when applying this to an actual system, please check the text of the laws and regulations and the latest guidance from the competent authority.
Decide what to record
These are the items that should be recorded from a document security perspective.
| Category | Items |
|---|---|
| Subject | Account identifier, affiliation, access IP, device information |
| Object | Document identifier, document grade, document name |
| Action | View, download, print, share, permission change, grade change |
| Time | Request time, processing completion time |
| Result | Success or failure, failure reason |
Failure records are especially important. Repeated attempts at unauthorized access are a signal that must be detected. If only successes are recorded, this signal is missed.
Things to decide together when defining the items:
- Record the values as they were at the time. For values that change later, such as document grade and affiliation, copy the value at the time instead of a reference. Otherwise, if you look it up after the grade has been lowered, the fact that "a confidential document was viewed" disappears from the record.
- Do not put document content in the log. If search terms, parts of the document body, or entire request bodies are recorded as they are, the audit log becomes another leak path. Record only identifiers and actions, and check the content in the source system.
- Define the behavior on recording failure by grade. When the log store fails, you have to decide whether to block or allow viewing. One way to divide it: for the confidential grade, viewing is not allowed if it cannot be recorded, and for the grades below it, entries are stored temporarily and resent. If this is not decided, behavior chosen arbitrarily by the implementer goes into production.
- Stamp the time in one place, on the server. If you use the time sent by the client or clocks that differ from server to server, the sequence cannot be reconstructed in an incident investigation. Include server time synchronization in the operational items.
Protect the log
If the audit log itself is tampered with, it is meaningless.
- Separate the party that records from the party that manages
- Make sure ordinary operators cannot modify or delete logs
- Keep them in separate storage
- Provide a means of integrity verification
The actions of administrator accounts are also subject to recording. If anything, the more privileged the account, the more important the record.
Detect tampering with a hash chain
One way to make "a means of integrity verification" concrete is a hash chain. When computing the hash of each record, the hash of the record immediately before it is included in the input. If a row in the middle is modified or deleted, the links after it no longer match, so the tampered position can be found simply by recomputing the whole chain.
Below is an example that assumes PostgreSQL 11 or later (it uses the built-in sha256 function and the EXECUTE FUNCTION syntax). The sequence number, time, and hashes are filled in by the trigger, not by the application.
CREATE TABLE audit_log (
seq bigint PRIMARY KEY,
occurred_at timestamptz NOT NULL,
actor_id text NOT NULL,
client_ip inet,
action text NOT NULL CHECK (action IN
('VIEW','DOWNLOAD','PRINT','SHARE','GRANT_CHANGE','GRADE_CHANGE')),
document_id text NOT NULL,
document_grade text NOT NULL,
result text NOT NULL CHECK (result IN ('ALLOWED','DENIED')),
deny_reason text,
prev_hash bytea NOT NULL,
row_hash bytea NOT NULL
);
-- Build the hash input in a fixed format that does not depend on session settings (time zone, etc.)
CREATE FUNCTION audit_payload(r audit_log) RETURNS text
LANGUAGE sql STABLE AS $$
SELECT jsonb_build_array(
r.seq,
to_char(r.occurred_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US'),
r.actor_id, host(r.client_ip), r.action, r.document_id,
r.document_grade, r.result, r.deny_reason
)::text;
$$;
CREATE FUNCTION audit_log_chain() RETURNS trigger
LANGUAGE plpgsql AS $$
DECLARE
last_row audit_log;
BEGIN
-- Serialize so that records arriving concurrently do not point to the same previous row
PERFORM pg_advisory_xact_lock(hashtext('audit_log_chain'));
SELECT * INTO last_row FROM audit_log ORDER BY seq DESC LIMIT 1;
NEW.seq := COALESCE(last_row.seq, 0) + 1;
NEW.occurred_at := clock_timestamp();
NEW.prev_hash := COALESCE(last_row.row_hash, sha256(''::bytea));
NEW.row_hash := sha256(NEW.prev_hash || convert_to(audit_payload(NEW), 'UTF8'));
RETURN NEW;
END;
$$;
CREATE TRIGGER audit_log_chain_before_insert
BEFORE INSERT ON audit_log
FOR EACH ROW EXECUTE FUNCTION audit_log_chain();
-- Allow only insert and select for the application account (app_user is an example name)
REVOKE ALL ON audit_log FROM PUBLIC;
GRANT INSERT, SELECT ON audit_log TO app_user;
Verification is done with a single query that recomputes the hash of every row and checks that the sequence numbers and the links to the previous and next rows are correct. Zero rows means everything is normal; if rows are returned, check from that sequence number.
SELECT seq
FROM (
SELECT a.seq, a.prev_hash, a.row_hash,
lag(a.seq) OVER (ORDER BY a.seq) AS previous_seq,
lag(a.row_hash) OVER (ORDER BY a.seq) AS previous_hash,
sha256(a.prev_hash || convert_to(audit_payload(a), 'UTF8')) AS recomputed
FROM audit_log a
) checked
WHERE row_hash <> recomputed
OR (previous_seq IS NOT NULL
AND (seq <> previous_seq + 1 OR prev_hash <> previous_hash))
ORDER BY seq;
You need to know what this method does not guarantee before using it.
- Within the same database, it only detects. The table owner and superusers are not affected by
REVOKE, and if someone modifies a row and then recomputes and writes all the hashes after it, the chain matches again. Deleting the most recent rows outright is also not revealed by the chain alone. - So anchor the end value of the chain outside the database. At regular intervals, record the last sequence number and hash in storage that the database administrator cannot write to, and compare against that value when verifying. Storage that cannot be overwritten or deleted during the retention period once written suits this purpose. For example, Amazon S3 Object Lock stores objects in a WORM model, and in compliance mode even the root user of the account cannot overwrite or delete an object version during the retention period.(Amazon S3 User Guide) Technically, the "separation of the party that records from the party that manages" mentioned earlier is achieved at this point.
- Recording order is serialized. The lock in the example makes records be written one at a time. In a system with a high viewing frequency this section can become a bottleneck, so consider a configuration in which records are put on a queue and a single writer appends them to the chain in order, or in which the chain is split into several branches. Throughput must be measured directly in the target environment to make the judgment.
- Changing the hash input format breaks verification of past records. When adding a column, give the format a version so that earlier rows can be verified with the earlier format. This is also why the example converts the time to a fixed UTC format. If the string varies with the session time zone, verification fails even without tampering.
- Deleting rows past their retention period cuts off the front of the chain. Keep the last hash of the deleted range in external storage and use that value as the starting point of the remaining chain for verification. This is also why the verification query does not separately check the
prev_hashof the first row.
Common errors in audit logs
| Symptom | Cause | Fix |
|---|---|---|
| In an incident investigation, "who viewed it" is available but "who attempted it" is not | Requests denied by the permission check never reach the logging code | Record at a single point that receives the result of the decision function (allowed or denied, and the reason) |
| File download records are fewer than actual downloads | Only the screen APIs log; the file path and signed URL issuance do not | List every path that sends documents out and check whether each one records |
| Every IP in the log is the same value | The address of the proxy or load balancer is recorded | Use only the client address passed on by a trusted proxy. Do not trust arbitrary request header values as they are |
| History queries become slow after a few months | Everything accumulates in a single table and there is no index matching the query conditions | Indexes matched to queries by document, by user, and by period; partitioning by period and a procedure for deleting records past the retention period |
| An operator cleans up the log table while responding to an outage | The operations account has delete permission and the retention period exists only in a document | Remove modify and delete permissions from the operations account; perform deletion only through a separate procedure that checks the retention period |
| Inspections are carried out but no evidence remains | There is only a query screen and no feature for recording inspection results | Also implement a feature that records the inspection date and time, the inspector, the scope checked, and the actions taken |
It is a record only if it can be viewed
If records are only accumulated and there is no means of querying them, they cannot be used when an incident occurs.
- Viewing history query by document
- Access history query by user
- Search by period and action type
- Anomalous pattern query — more document views than usual, bulk downloads, access outside working hours
For anomalous patterns, it is better to set up alerts. Becoming aware at the time of occurrence leaves more room to respond than checking after the fact. However, if the thresholds are set loosely and alerts pour in every day, the person in charge soon stops checking. Set the thresholds relative to each user's usual usage, and keep them as configuration values so they can be adjusted during operation. The structure in which alerts get ignored is covered separately in The Point Where Notifications Start Getting Ignored — Designing a Delivery System.
5. Build a path for cases where documents must be taken out
Strengthening controls alone blocks legitimate work as well. Situations where materials must be provided externally do exist.
- Submission to supervisory authorities
- Handing over deliverables to a contract counterparty
- Providing materials for legal advice
- Responding to audits
An official export procedure is needed for such cases.
| Item | What to define |
|---|---|
| Request | Who requests it, and with what form |
| Approval | Approvers by grade |
| Processing | Watermark application, validity period, password setting |
| Record | Reason for export, recipients, list of documents |
| Follow-up | Confirmation of return or destruction |
If the procedure is too heavy, people send things by personal email or messenger. Minimizing the approval steps and processing requests quickly actually helps maintain control.
Setting a validity period on files that go outside is another approach that is used: the file is configured not to open after a certain period. It is not complete, but it reduces the situation where old materials keep circulating.
6. Reconcile the points that conflict with work
Security controls inevitably conflict with convenience at work. If this reconciliation is not done at the design stage, the controls come undone during operation.
Excessive control creates workarounds
These are commonly observed.
- Downloads are blocked, so people capture the screen and share it
- Sending external email is blocked, so people send through personal messengers
- Approval is required every time, so people obtain files in bulk in advance and use them
- Access is inconvenient, so people keep copies in personal storage
When controls get in the way of work too much, people look for workarounds. And a workaround is a path with neither control nor records. As a result, the level of security drops further.
Consider partners and external users
A system used only by internal employees is actually the simple case. Once there are external users, the design changes.
- Account issuance and revocation procedures
- Limiting the scope of access by affiliated organization
- Blocking everything at once when the contract ends
- The level of control to apply to external users
For external users, it is common to apply stronger controls while keeping the scope narrow: designate only the documents they need to view, and restrict downloads in principle.
Prepare an exception procedure
Situations where controls must be relaxed will certainly arise.
- Who can approve it?
- How long is it valid?
- What records are kept?
- Is it reviewed afterwards?
A design that allows no exceptions is actually the dangerous one. Without an official exception path, an unofficial path appears, and it is not recorded.
7. Frequently asked questions
Can screen capture be blocked completely?
No. Depending on the environment, it can be made partially difficult.
- With a dedicated application, there are cases where operating-system-level capture restriction features can be used
- In a web browser environment, restriction is difficult
- Using a virtual desktop environment increases the number of control points
The scope of the features provided by operating systems is stated in the official documentation, and so are their limits.
| Environment | Feature | Scope and limits stated in the official documentation |
|---|---|---|
| Android app | Set FLAG_SECURE on the window |
Screenshots come out as a blank screen, and the window is not shown on non-secure displays (such as screen casting). On Android 11 (API 30) and lower, it is noted that it may not work reliably depending on the device |
| Windows desktop app | SetWindowDisplayAffinity |
WDA_MONITOR blanks the window content on paths other than the monitor, and WDA_EXCLUDEFROMCAPTURE (Windows 10 version 2004 or later) excludes the window itself from capture. It is explicitly stated that this is not a security feature or DRM and that protection is not guaranteed in cases such as photographing the screen |
The sources are the Android developer documentation and Microsoft Learn, respectively. Both features are something an app requests from the operating system for its own window, so they can be used only when building a dedicated application and cannot be applied to web pages opened in a browser. If capture blocking goes into the requirements, write the "supported operating systems and versions, and the capture paths covered" as a table, and decide whether viewing itself is to be allowed in other environments.
Whatever the method, photographing the screen with another device cannot be prevented. So when you accept capture blocking as a requirement, the applicable environments and the limits must be stated in the document.
Can security features alone be added to an existing document system?
It depends on the structure of the existing system. Points to check:
- Is document access through a single path, or are there several paths?
- Are files stored in a directly accessible location?
- Can the existing permission scheme be extended?
- Are there points available at which logs can be written?
If files are in a directly accessible location, that has to be cleaned up first. However much you control the screens, someone who knows the file path can bypass them.
Does applying encryption solve it?
Encryption is necessary but not sufficient. The problems it solves and the ones it does not are different.
- Solved — theft of storage media, eavesdropping in transit, leakage of backup files
- Not solved — a user with legitimate permission taking out the decrypted content
A large share of document leaks are the second case. Apply encryption as a baseline, but permission management and a means of tracing must be in place alongside it.
How far is it appropriate to implement?
It is decided by the sensitivity of the documents and the scale of damage if they leak. The criteria for judgment:
- What damage occurs if it leaks?
- Which paths are currently open?
- What are the adoption cost and work impact of each control?
Applying the highest level to every document is inefficient. Dividing documents into grades and concentrating on the higher grades is more cost-effective. In most cases, the documents that actually qualify as confidential are a fraction of the total.
8. Checklist before starting
Requirements definition
- Have the control and completeness for each leak path been agreed?
- Are the areas that cannot be blocked stated in the document?
- Are the document grade scheme and the level of control per grade defined?
- Is there a party that assigns grades and a procedure for changing them?
Permission design
- Is the relationship between role-based permissions and individual designation defined?
- Do temporary permissions have an expiry date?
- Is there a procedure for revoking permissions on department transfer and resignation?
- Is there a periodic permission review procedure?
- Is server-side permission verification performed on every access path?
- Is there no path that allows direct access to files?
- Are the decision order for conflicting rules and the "deny when no rule matches" principle implemented in one place?
- Do search results, thumbnails, and conversion outputs go through the same decision?
- Are permission changes reflected in sessions and caches immediately?
- Are the expiry time of signed URLs and the method of recording their issuance defined?
- Is the retention period for records of granting, changing, and cancelling permissions defined?
Watermark
- Are the displayed content and the display method defined?
- Is the handling of printouts designed separately?
- If file embedding is used, has it been reviewed from a personal information perspective?
- Is the watermark for higher-grade documents composited on the server?
- Has a test been run to read the identification code from photographed and downscaled images?
- Does the retention period of the viewing records looked up by identification code match the period for which tracing is needed?
Audit log
- Has the retention period under the applicable laws and regulations been checked?
- Have the effective date of the amended Notice and the scope of access record retention (whether it includes those who are not internal employees) been checked?
- Are failed access attempts recorded?
- Are administrator actions also subject to recording?
- Are there measures to prevent log tampering?
- Is the reference value used for integrity verification kept somewhere the database administrator cannot modify?
- Is the behavior on log storage failure defined by grade?
- Is there a procedure for deleting records past the retention period?
- Is there a means of querying history by document and by user?
- Are there alert thresholds for anomalous patterns?
Operations
- Are the exception approval procedure and the method of recording defined?
- Is the level of control for external users defined separately?
- Has the impact of the controls on work been reviewed?
Closing
The most important thing in document security design is making clear at the outset what can and cannot be done.
Permission control can block reliably. Downloading and printing can be controlled to a considerable extent. Screen capture is limited depending on the environment. Photographing with another device cannot be prevented; it can only be traced.
If this distinction is agreed at the requirements stage, the subsequent design becomes the work of deciding which controls to apply to each path and to what extent. Conversely, if "complete blocking" is set as the goal, an argument that cannot be settled remains at the acceptance stage.
We have built a watermark DRM solution that combines document permission management with printout watermarks and applied it to an environment of 3,000 cumulative users. If you need a review at the stage of turning document security requirements into a system, please contact us.
Related articles
- How Scope Grows in SI Projects and How to Design Change Management to Contain It
- Where External System Integrations Fail — Principles of Interface Design
- The Point Where Notifications Start Getting Ignored — Designing a Delivery System
References
- Korean Law Information Center (국가법령정보센터), Standards for Ensuring the Safety of Personal Information (Personal Information Protection Commission Notice No. 2025-9, partially amended October 31, 2025) (in Korean) — https://www.law.go.kr/admRulLsInfoP.do?chrClsCd=010202&admRulSeq=2100000265956
- Korean Law Information Center, Standards for Ensuring the Safety of Personal Information (Personal Information Protection Commission Notice No. 2023-6, previous provisions) (in Korean) — https://www.law.go.kr/admRulLsInfoP.do?chrClsCd=010202&admRulSeq=2100000229672
- Personal Information Protection Commission, Guide to the Standards for Ensuring the Safety of Personal Information (October 2024, based on the provisions before the Notice No. 2025-9 amendment) (in Korean) — https://business.cch.com/CybersecurityPrivacy/KoreanGuidetotheStandardsforEnsuringtheSafetyofPersonalInformationOctober2024.pdf
- NIST, SP 800-162 "Guide to Attribute Based Access Control (ABAC) Definition and Considerations" — https://nvlpubs.nist.gov/nistpubs/specialpublications/NIST.SP.800-162.pdf
- AWS, Amazon S3 User Guide "Download and upload objects with presigned URLs" — https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html
- AWS, Amazon S3 User Guide "Locking objects with Object Lock" — https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html
- Android Developers, "Secure sensitive activities" — https://developer.android.com/security/fraud-prevention/activities
- Microsoft Learn, "SetWindowDisplayAffinity function (winuser.h)" — https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity
- PostgreSQL documentation, "Binary String Functions and Operators" — https://www.postgresql.org/docs/current/functions-binarystring.html

