diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/XmlAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/XmlAttrFilter.java
index 838d6bf7c..7c25766b1 100644
--- a/src/main/java/com/hubspot/jinjava/lib/filter/XmlAttrFilter.java
+++ b/src/main/java/com/hubspot/jinjava/lib/filter/XmlAttrFilter.java
@@ -8,6 +8,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.regex.Pattern;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -37,6 +38,11 @@
 )
 public class XmlAttrFilter implements Filter {
 
+  // See https://html.spec.whatwg.org/#attribute-name-state Don't allow characters that would change the attribute name/value state
+  private static final Pattern ILLEGAL_ATTRIBUTE_KEY_PATTERN = Pattern.compile(
+    "[\\s/>=]"
+  );
+
   @Override
   public String getName() {
     return "xmlattr";
@@ -53,6 +59,11 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args)
     List<String> attrs = new ArrayList<>();
 
     for (Map.Entry<String, Object> entry : dict.entrySet()) {
+      if (ILLEGAL_ATTRIBUTE_KEY_PATTERN.matcher(entry.getKey()).find()) {
+        throw new IllegalArgumentException(
+          String.format("Invalid character in attribute name: %s", entry.getKey())
+        );
+      }
       attrs.add(
         new StringBuilder(entry.getKey())
           .append("=\"")
diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/XmlAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/XmlAttrFilterTest.java
index 7f6f0eebc..fb916766f 100644
--- a/src/test/java/com/hubspot/jinjava/lib/filter/XmlAttrFilterTest.java
+++ b/src/test/java/com/hubspot/jinjava/lib/filter/XmlAttrFilterTest.java
@@ -2,8 +2,11 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import com.google.common.collect.ImmutableList;
 import com.hubspot.jinjava.BaseJinjavaTest;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
@@ -27,4 +30,27 @@ public void testXmlAttr() {
     assertThat(dom.select("ul").attr("id")).isEqualTo("list-42");
     assertThat(dom.select("ul").attr("missing")).isEmpty();
   }
+
+  @Test
+  public void itDoesNotAllowInvalidKeys() {
+    List<String> invalidStrings = ImmutableList.of("\t", "\n", "\f", " ", "/", ">", "=");
+    invalidStrings.forEach(invalidString ->
+      assertThat(
+        jinjava
+          .renderForResult(
+            String.format("{{ {'%s': 'foo'}|xmlattr }}", invalidString),
+            Collections.emptyMap()
+          )
+          .getErrors()
+      )
+        .matches(templateErrors ->
+          templateErrors.size() == 1 &&
+          templateErrors
+            .get(0)
+            .getException()
+            .getCause()
+            .getCause() instanceof IllegalArgumentException
+        )
+    );
+  }
 }