Screen file uploads for malicious content
Run a firewall function that sends each upload to a model on AI Inference, deny the files it flags, and block their senders with a network list.
An application team accepts documents from users, such as PDFs in onboarding, claims, or support flows. A file can carry a malicious payload, or text written to manipulate the AI systems that read it later, and a rule that reads only the request’s envelope sees none of it. This page configures a function on the firewall that sends each upload to a model on AI Inference and denies the files the model flags, a network list that blocks their senders, and the stream that carries each verdict to the SIEM. The result is measured by the malicious uploads blocked before the application, the latency added per upload, and the false-positive rate.
This use case does not cover antivirus scanning of stored files, or general WAF protection, which Protect web applications from OWASP Top 10 and zero-day attacks covers.
Prerequisites
- An Azion account. If you do not have one, create an account.
- An application that serves the upload route through a connector and a workload. To create them, refer to Applications quickstart.
- A firewall bound to that workload’s deployment, with Functions and Network Shield turned on in Main Settings › Modules. To bind it, refer to Bind a firewall to a workload.
- Mistral 3 Small, whose id is
casperhansen-mistral-small-24b-instruct-2501-awq, available to the account. For the models AI Inference runs, refer to AI models.
Required products
| The upload route needs | Which means | Product | Documented in |
|---|---|---|---|
| Each upload read before the application receives it | A firewall function, run by a rule on the upload route, that reads the request body | Functions | Scan file uploads with an AI Inference firewall function |
| A verdict on the content of each file | A call to a model through Azion.AI.run, with a prompt that asks for true or false | AI Inference | Model invocation |
| The senders of malicious files refused on every route | A network list that a deny rule reads through the Network criterion | Network Shield | Network Lists |
| Each verdict in the team’s SIEM | A stream of the Functions data source, which carries the lines the function logs, to the SIEM’s endpoint | Data Stream | Debug functions with Data Stream |
Reference architecture
This page builds the AI-assisted upload inspection pipeline: the firewall sends each upload to a function that asks a model whether it is malicious, and applies the verdict in the same request.
Read the diagram at the function. The rules before it are ordinary firewall decisions, made from the request’s envelope: the sender’s address and the route. The function is where the design reads the file itself, and the model’s verdict is the one new dependency the design adds to the request path. Everything after the function follows from that verdict, and the dotted loop through Data Stream turns one malicious file into a block on its sender.
Dataflow
- A user sends a file to
/upload. The first firewall rule compares the user’s address withupload-senders, and a listed sender receives403on every route. - The second rule runs the
upload-inspectionfunction on every request to/upload. - The function reads the request body and sends it to the model through
Azion.AI.run, with a prompt that asks fortruewhen the content is malicious. A file whoseContent-Typestarts withimage/continues without a model call. - When the model answers
true, the function writes a warning line and denies the request with403, before the application receives it. Otherwise the upload continues to the application. - Data Stream sends the function’s lines to the SIEM.
- A verdict’s request ID leads to the sender’s address, which is then added to
upload-senders, the list the first rule reads.
Platform Resources
- firewall: the Platform Resource that routes uploads to the function. One rule runs the function on the upload route, and another denies the senders in the network list on every route.
- Functions: runs the inspection function in the
firewallexecution environment. It extracts the content from the request, calls the model, and applies the verdict in the same request, so a refused file stays outside the application. - AI Inference: runs the model that classifies the content. The prompt in the function’s arguments sets what the model treats as malicious, so the policy changes without new code.
- Object Storage: holds a quarantine bucket, where a refused upload can be kept for an analyst to review instead of disappearing. The function on this page denies the file and writes it to no bucket.
- Network Shield: holds the sender blocklist, the network list whose deny rule refuses a flagged sender on every route.
- Data Stream: sends the function’s verdict lines to the endpoint the SIEM reads.
- SIEM: the integration that correlates the verdicts with the request records and the team’s other sources, and that turns a verdict into a blocklist entry.
How the design works
This section explains why the inspection function, the rule that runs it on the upload route, and the sender blocklist take their shape.
The inspection function
The function registers a firewall handler, so it runs on the firewall, before the request reaches the application. It reads the body as text and sends it to the model as the user message, with the prompt from the instance’s arguments as the system message. temperature is 0 and max_tokens is 1024, the values the scan-uploads guide runs: the verdict is one word, so the call asks for no variation. The function compares the answer with the string true, so the prompt tells the model to answer with true or false alone.
The function handles three cases by itself. A Content-Type that starts with image/ continues with no model call. A Content-Type that names base64 is decoded before the call. Any error, in the call or in the parsing, writes an error line and lets the request continue, so the route keeps working when the model does not answer.
action names the event method the function calls on a malicious file: deny answers 403, and drop would close the request with no answer.
The function on the upload route
The function runs only where a rule runs its instance, so the rule matches ${request_uri} starts with /upload. Every other route skips the model call, which keeps the latency and the AI Inference usage on the uploads alone. Create this rule after the sender blocklist’s rule, or move it below that rule, so a listed sender is refused before the model reads its file.
The sender blocklist
A sender whose file the model flagged is refused on every route of the workload, /upload included, by a deny rule that reads the upload-senders list. The function logs the verdict, not the address. In Data Stream, each line of the Functions data source carries the request’s $request_id, and the Applications data source carries $remote_addr for the same $request_id. A person or the SIEM joins the two and adds the address to the list. To let the SIEM write the list, refer to Block attackers automatically from SIEM detections.
Each entry carries a due date and a comment. The due date, 7 days after the upload, ends the block for an address that a different user may hold later. The comment names the request ID, so an analyst finds the verdict behind the block. A due date takes effect only at the next write of the list’s items, as Block addresses until a date explains.
Measuring results
| Metric | Where to read it | What working looks like |
|---|---|---|
| Malicious uploads blocked before the application | The warning lines that read [AI] true, in the functionConsoleEvents dataset of Real-Time Events or in the SIEM | Each blocked upload has a line, and no flagged file appears in the application’s own storage |
| Latency added per upload | Request Time of the requests to /upload, in the HTTP Requests data source, against the same route before the rule | Stays within what the upload flow tolerates, under the AI Inference limits |
| False-positive rate | The blocked uploads that the team’s analysts review and label as safe, against all blocked uploads | Falls as the prompt in the instance’s arguments is tuned on the labeled files |
Best practices
- Watch the error lines, because the function fails open. On any error, the function writes an error line and lets the upload continue. A model that stops answering therefore shows as error lines, not as blocked uploads. Alert on lines that read
Error handling request:. - Remove the image branch when the route accepts no images. The function lets any request whose
Content-Typestarts withimage/continue without a model call, and the client sets that header. A route that receives only documents loses nothing when the branch goes. - Keep a WAF rule in Blocking off the upload route. WAF parses a request body only up to 131,072 bytes and refuses a larger one with
400in Blocking, so a WAF rule on/uploadrefuses every file larger than 128 KiB before the model reads it. For the bound, refer to Request body parsing. - Tune the prompt, not the code. The prompt lives in the instance’s arguments, so a change to it reaches traffic in about 105 seconds, with no new function. Feed the files the analysts label back into the prompt.
- Put a due date on every sender entry. An address can pass to another user, and a block with no end refuses that user too.