# Detecting harmful LaTeX code [duplicate]

I'm writing a website which allows users to enter some text in predefined text fields. This text gets passed on the server where a LaTeX document is created (containing the text entered by the user). The server finally returns a compiled LaTeX document (a pdf) to the user. Note: the user doesn't enter the whole tex document, only parts.

My problem: how can I make sure that the entered text will not harm my server? I.e. how to detect harmful LaTeX code?

Some examples:

• The user entered an infinite loop written in LaTeX, the server can't compile the document.

• The user entered a shell script which will be executed from the tex file when compiling, potentially crashing my server.

Is my best alternative to blacklist any LaTeX code? Is detecting \ followed by a non-space enough to block any potentially harmful LaTeX code?

## marked as duplicate by Martin Schröder, Andrew, user13907, user36296, WernerOct 26 '16 at 0:40

• Disable all shell-escape (including the restricted ones) and run LuaTeX with the --safer option (or don't use it at all). Of course, never run TeX as root. – Henri Menke Oct 20 '16 at 11:30
• 1. Sandbox. 2. Whitelist very few commands. Even \begin may not be safe: try \begin{input}{/etc/passwd} inside the document (Linux). – Bruno Le Floch Oct 20 '16 at 12:24
• Another option if you need only very basic formatting commands etc: don't accept TeX input. Accept markdown and pass through pandoc to convert to LaTeX, trimming any extraneous material (preamble). – Chris H Oct 20 '16 at 12:58
• @BrunoLeFloch To be clear: that won't reveal any passwords. It would, however, provide a list a user names. But if the security of the system depends on users not reading a file marked world-readable, there's a problem. On a secure system, this cannot be a real threat. Users can cat /etc/passwd on multi-user systems, but that doesn't really compromise the system. In theory, knowing user names makes an attack slightly easier, but strong passwords and standard precautions mean that makes little difference. The attacker already knows there's a root user, for example. – cfr Oct 20 '16 at 14:59
• Great, now we need a Anti-LaTeX-Malware program written in LaTeX as well... – Tobias Kienzler Oct 20 '16 at 19:59

web2c based tex's have quite a lot of customisation to control this. As is a well known theorem of Turing, it's not possible to detect all possible infinite loops in any non trivial programming language, so if the tex code is \def\x{\x}\x it will loop forever, however any web hosting setup should allow you to specify time limits for any forked processes so that isn't really a problem, you can always kill the job after whatever time limit you want to set.

running scripts is not allowed by default so your second concern is only an issue if you allow it to run arbitrary user specified commands, so don't do that:-)

You may also want to clamp down on the ability to read files outside of the input tree by banning reading of /etc/passwd etc (writing such files is again prevented by default)

the texmf.cnf controlling your text installation will have

% Do we allow TeX \input or \openin (openin_any), or \openout
% (openout_any) on filenames starting with .' (e.g., .rhosts) or
% outside the current tree (e.g., /etc/passwd)?
% a (any)        : any file can be opened.
% r (restricted) : disallow opening dot files
% p (paranoid)   : as r' and disallow going to parent directories, and
%                  restrict absolute paths to be under $TEXMFOUTPUT. openin_any = a openout_any = p  you may want to make openin_any also p Other than that tex is as safe as anything else you can do, it can not spawn any new commands, it can not write anywhere other than the directory it is started from (and subdirectories of that) and it can not read any files out of the specified input path. \endinput% this file is anti-social if this line is removed \makeatletter \ProvidesFile{xxx}[\noexpand\ver@xxx] \ProvidesFile{xxx}[\ver@xxx] \documentclass{article} \begin{document} \end{document}  • Why openin_any = a? Doesn't that allow to read /etc/passwd? – Bruno Le Floch Oct 20 '16 at 12:50 • @BrunoLeFloch it is what it is (yes, if you have read permission for that) – David Carlisle Oct 20 '16 at 12:58 • @BrunoLeFloch So what? What does reading /etc/passwd get you exactly? – cfr Oct 20 '16 at 15:02 • That is a mis-characterization of the halting problem. (As you probably know). It is not possible for a program to detect all "infiniteloops", but it is very possibly for a program to detect many infinite loops. Though in this case, I guess it doesn't make much different – Lyndon White Oct 20 '16 at 20:10 • @Oxinabox er yes it's what I meant of course, it is definitely possible to detect some loops, there is one in the code at the end of the answer for example:-) I'll re-word a bit. – David Carlisle Oct 20 '16 at 20:13 Detecting anything starting with a backslash is probably going too far. WIthout knowing the content of your documents, \emph{}, \textsuperscript{}, $\mu\$m may all be reasonable.

You should certainly disable shell-escape to prevent arbitrary command being run.

You should probably run the compiler in a sandbox of some kind (heavily dependent on your host system so I couldn't give details even if I was an expert). You can also have a watchdog to kill the process if it runs unreasonably long (it sounds like you have a good idea of the job structure and could predict the runtime). Setting text is quick so an abnormally large input shouldnt increase the time by much.

Most attempts to hang a LaTeX compiler would be more likely to cause it to abort -- possible with a "TeX capacity exceeded" error. But of course it might take some time to do that. So a reasonable code-validating step might be to check for and block \def, \newcommand and equivalents. It would annoy some users (like many people here) but would make it a little harder to (deliberately or otherwise) hang the compiler by things like uncontrolled recursion. There are ways round this using \begin{def} so it would probably be a good idea to whitelist any environments that we can \begin.

• \begin{def}\x{a\par\x}\x gives an infinite document (each paragraph consists of a single a). – Bruno Le Floch Oct 20 '16 at 12:48
• @BrunoLeFloch, sneaky use of \begin to avoid \def is quite clever. A watchdog/process time limit should kill the process eventually but limiting what you can \begin is starting to look like a good idea (my comment to your comment under the Q). I'll add something. – Chris H Oct 20 '16 at 13:00
• @ChrisH trying to parse the file to spot bad commands is entirely pointless, anyone trying to put bad stuff can hide it, how would you parse the valid tex file found at ctan.org/pkg/xii to work out what it is doing? – David Carlisle Oct 20 '16 at 13:18
• @DavidCarlisle not entirely pointless. Proper sandboxing, disabling scripts, limiting process time and permissions are your main lines of defence (as you've expressed more clearly and in more detail than I could have done). But you could reduce the load on your server blocking by certain commands which could easily be used to cause problems. Your xii.tex prompts another idea: setting another char to catcode 0 to get round previous filtering ideas. A whitelist would be better than a blacklist, but would be huge if things like amssymb are available. – Chris H Oct 20 '16 at 13:28
• @ChrisH I added a different example to my answer... – David Carlisle Oct 20 '16 at 13:38